{
Ultibo WiFi (IEEE 802.11 / WPA) interface unit.

Copyright (C) 2018 - SoftOz Pty Ltd.

Arch
====

 <All>

Boards
======

 <All>

Licence
=======

 LGPLv2.1 with static linking exception (See COPYING.modifiedLGPL.txt)
 
Credits
=======

 Information for this unit was obtained from:

  Linux - \include\net\cfg80211.h
          \include\net\mac80211.h
          \include\linux\ieee80211.h

          \net\mac80211\rx.c
  
          \net\wireless\chan.c
          \net\wireless\util.c
          
References
==========

 Based on information from sources of WPA supplicant, the Linux kernel and others
 
 Linux Wireless - https://wireless.wiki.kernel.org/en/developers/Documentation/ieee80211
                  https://wireless.wiki.kernel.org/en/developers/documentation/mac80211
                  
WiFi
====

 Terminology 
 -----------
 
 IEEE80211 - https://en.wikipedia.org/wiki/IEEE_802.11
             https://en.wikipedia.org/wiki/IEEE_802.11a-1999
             https://en.wikipedia.org/wiki/IEEE_802.11b-1999
             https://en.wikipedia.org/wiki/IEEE_802.11g-2003
             https://en.wikipedia.org/wiki/IEEE_802.11n-2009
             
             https://en.wikipedia.org/wiki/Wireless_LAN
             https://en.wikipedia.org/wiki/Wi-Fi_Direct
    
    
 WEP - https://en.wikipedia.org/wiki/Wired_Equivalent_Privacy
 
 WPA - https://en.wikipedia.org/wiki/Wi-Fi_Protected_Access
 
 WPA2 - https://en.wikipedia.org/wiki/IEEE_802.11i-2004
 
 MIC - https://en.wikipedia.org/wiki/Message_authentication_code
 
 TKIP - https://en.wikipedia.org/wiki/Temporal_Key_Integrity_Protocol
 
 CCMP - https://en.wikipedia.org/wiki/CCMP
 
 EAPOL - https://en.wikipedia.org/wiki/IEEE_802.1X
 
 MLME (MAC Layer Management Entity)
 
 Driver Porting
 --------------
 
  Initialization

   For USB drivers USBDriverBind is equivalent to Linux usb_driver.probe (Mandatory)
                   USBDriverUnbind is equivalent to Linux usb_driver.disconnect  (Mandatory)

  NetworkDeviceOpen is equivalent to Linux ieee80211_ops.start (Mandatory)
  NetworkDeviceClose is equivalient to Linux ieee80211_ops.stop (Mandatory)
   
  WiFiDeviceConfigure is equivalent to Linux ieee80211_ops.config (Mandatory)
  WiFiDeviceConfigureFilter is equivalent to Linux ieee80211_ops.configure_filter (Mandatory)
  WiFiDeviceConfigureInterface is equivalent to Linux ieee80211_ops.add_interface/remove_interface (Mandatory) (Note: Multiple virtual interfaces are not supported)
           
  WiFiDeviceCreate/WiFiDeviceCreateEx is equivalent to Linux mac80211 - ieee80211_alloc_hw/ieee80211_alloc_hw_nm
  WiFiDeviceDestroy is equivalent to Linux mac80211 - ieee80211_free_hw
   
  WiFiDeviceRegister is equivalent to Linux mac80211 - ieee80211_register_hw
  WiFiDeviceDeregister is equivalent to Linux mac80211 - ieee80211_unregister_hw
   
 Driver Information
 ------------------
 
  https://en.wikipedia.org/wiki/Comparison_of_open-source_wireless_drivers
  
  https://wireless.wiki.kernel.org/en/users/Drivers
  
  
}

{$mode delphi} {Default to Delphi compatible syntax}
{$H+}          {Default to AnsiString}
{$inline on}   {Allow use of Inline procedures}

unit WiFi;

interface

uses GlobalConfig,GlobalConst,GlobalTypes,GlobalSock,Platform,Threads,Devices,Network,Transport,Crypto,SysUtils;

//To Do //See: http://w1.fi/wpa_supplicant/
        //See: http://w1.fi/wpa_supplicant/devel/
        //See: http://w1.fi/wpa_supplicant/devel/porting.html
        //See: http://w1.fi/wpa_supplicant/devel/driver_wrapper.html
        //See: http://w1.fi/wpa_supplicant/devel/code_structure.html
        //See: http://w1.fi/cgit
                      
//To Do //In general terms WPA appears to the network stack as a Transport layer
        //sending and receiving 2 specific packet types:
        //EAP-over-LAN (EAPOL) $888E and RSN pre-authentication $88C7
                   
//To Do //radiotap support
        //See: http://www.radiotap.org/
        //See: /include/net/ieee80211_radiotap.h               
        //See: \net\mac80211\rx.c
        
{==============================================================================}
{Global definitions}
{$INCLUDE GlobalDefines.inc}

{==============================================================================}
const
 {IEEE80211 specific constants} {As per Linux ieee80211.h}
 
 {DS bit usage
 
 TA = transmitter address
 RA = receiver address
 DA = destination address
 SA = source address
 
 ToDS    FromDS  A1(RA)  A2(TA)  A3      A4      Use
 -----------------------------------------------------------------
 0       0       DA      SA      BSSID   -       IBSS/DLS
 0       1       DA      BSSID   SA      -       AP -> STA
 1       0       BSSID   SA      DA      -       AP <- STA
 1       1       RA      TA      DA      SA      unspecified (WDS)}
 
 IEEE80211_FCS_LEN = 4;
 
 IEEE80211_FCTL_VERS             = $0003;
 IEEE80211_FCTL_FTYPE            = $000c;
 IEEE80211_FCTL_STYPE            = $00f0;
 IEEE80211_FCTL_TODS             = $0100;
 IEEE80211_FCTL_FROMDS           = $0200;
 IEEE80211_FCTL_MOREFRAGS        = $0400;
 IEEE80211_FCTL_RETRY            = $0800;
 IEEE80211_FCTL_PM               = $1000;
 IEEE80211_FCTL_MOREDATA         = $2000;
 IEEE80211_FCTL_PROTECTED        = $4000;
 IEEE80211_FCTL_ORDER            = $8000;
 IEEE80211_FCTL_CTL_EXT          = $0f00;
 
 IEEE80211_SCTL_FRAG             = $000F;
 IEEE80211_SCTL_SEQ              = $FFF0;
 
 IEEE80211_FTYPE_MGMT            = $0000;
 IEEE80211_FTYPE_CTL             = $0004;
 IEEE80211_FTYPE_DATA            = $0008;
 IEEE80211_FTYPE_EXT             = $000c;
 
 {Management}
 IEEE80211_STYPE_ASSOC_REQ       = $0000;
 IEEE80211_STYPE_ASSOC_RESP      = $0010;
 IEEE80211_STYPE_REASSOC_REQ     = $0020;
 IEEE80211_STYPE_REASSOC_RESP    = $0030;
 IEEE80211_STYPE_PROBE_REQ       = $0040;
 IEEE80211_STYPE_PROBE_RESP      = $0050;
 IEEE80211_STYPE_BEACON          = $0080;
 IEEE80211_STYPE_ATIM            = $0090;
 IEEE80211_STYPE_DISASSOC        = $00A0;
 IEEE80211_STYPE_AUTH            = $00B0;
 IEEE80211_STYPE_DEAUTH          = $00C0;
 IEEE80211_STYPE_ACTION          = $00D0;
 
 {Control}
 IEEE80211_STYPE_CTL_EXT         = $0060;
 IEEE80211_STYPE_BACK_REQ        = $0080;
 IEEE80211_STYPE_BACK            = $0090;
 IEEE80211_STYPE_PSPOLL          = $00A0;
 IEEE80211_STYPE_RTS             = $00B0;
 IEEE80211_STYPE_CTS             = $00C0;
 IEEE80211_STYPE_ACK             = $00D0;
 IEEE80211_STYPE_CFEND           = $00E0;
 IEEE80211_STYPE_CFENDACK        = $00F0;
 
 {Data}
 IEEE80211_STYPE_DATA                    = $0000;
 IEEE80211_STYPE_DATA_CFACK              = $0010;
 IEEE80211_STYPE_DATA_CFPOLL             = $0020;
 IEEE80211_STYPE_DATA_CFACKPOLL          = $0030;
 IEEE80211_STYPE_NULLFUNC                = $0040;
 IEEE80211_STYPE_CFACK                   = $0050;
 IEEE80211_STYPE_CFPOLL                  = $0060;
 IEEE80211_STYPE_CFACKPOLL               = $0070;
 IEEE80211_STYPE_QOS_DATA                = $0080;
 IEEE80211_STYPE_QOS_DATA_CFACK          = $0090;
 IEEE80211_STYPE_QOS_DATA_CFPOLL         = $00A0;
 IEEE80211_STYPE_QOS_DATA_CFACKPOLL      = $00B0;
 IEEE80211_STYPE_QOS_NULLFUNC            = $00C0;
 IEEE80211_STYPE_QOS_CFACK               = $00D0;
 IEEE80211_STYPE_QOS_CFPOLL              = $00E0;
 IEEE80211_STYPE_QOS_CFACKPOLL           = $00F0;
 
 {Extension, added by 802.11ad}
 IEEE80211_STYPE_DMG_BEACON              = $0000; 

 {Control extension - for IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTL_EXT}
 IEEE80211_CTL_EXT_POLL          = $2000;
 IEEE80211_CTL_EXT_SPR           = $3000;
 IEEE80211_CTL_EXT_GRANT         = $4000;
 IEEE80211_CTL_EXT_DMG_CTS       = $5000;
 IEEE80211_CTL_EXT_DMG_DTS       = $6000;
 IEEE80211_CTL_EXT_SSW           = $8000;
 IEEE80211_CTL_EXT_SSW_FBACK     = $9000;
 IEEE80211_CTL_EXT_SSW_ACK       = $a000;
 
 //To Do //IEEE80211_SN_MASK/IEEE80211_MAX_SN/IEEE80211_SN_MODULO etc from ieee80211.h
 
 //To Do //IEEE80211_SEQ_TO_SN etc from ieee80211.h
 
 {Miscellaneous IEEE 802.11 constants}
 IEEE80211_MAX_FRAG_THRESHOLD    = 2352;
 IEEE80211_MAX_RTS_THRESHOLD     = 2353;
 IEEE80211_MAX_AID               = 2007;
 IEEE80211_MAX_TIM_LEN           = 251;
 IEEE80211_MAX_MESH_PEERINGS     = 63;
 {Maximum size for the MA-UNITDATA primitive, 802.11 standard section 6.2.1.1.2.
   802.11e clarifies the figure in section 7.1.2. The frame body is up to 2304 octets long (maximum MSDU size) plus any crypt overhead}
 IEEE80211_MAX_DATA_LEN          = 2304;
 {802.11ad extends maximum MSDU size for DMG (freq > 40Ghz) networks to 7920 bytes, see 8.2.3 General frame format}
 IEEE80211_MAX_DATA_LEN_DMG      = 7920;

 {30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS}
 IEEE80211_MAX_FRAME_LEN         = 2352;
 IEEE80211_MAX_SSID_LEN          = 32;
 IEEE80211_MAX_MESH_ID_LEN       = 32;
 
 IEEE80211_FIRST_TSPEC_TSID      = 8;
 IEEE80211_NUM_TIDS              = 16;
 
 IEEE80211_MAX_CHAINS            = 4;
 
 {Number of user priorities 802.11 uses}
 IEEE80211_NUM_UPS               = 8;
 
 IEEE80211_QOS_CTL_LEN           = 2;
 
 IEEE80211_QOS_CTL_TAG1D_MASK            = $0007; {1d tag mask}
 IEEE80211_QOS_CTL_TID_MASK              = $000f; {TID mask}
 IEEE80211_QOS_CTL_EOSP                  = $0010; {EOSP}
 IEEE80211_QOS_CTL_ACK_POLICY_NORMAL     = $0000; {ACK policy}
 IEEE80211_QOS_CTL_ACK_POLICY_NOACK      = $0020;
 IEEE80211_QOS_CTL_ACK_POLICY_NO_EXPL    = $0040;
 IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK   = $0060;
 IEEE80211_QOS_CTL_ACK_POLICY_MASK       = $0060;
 IEEE80211_QOS_CTL_A_MSDU_PRESENT        = $0080; {A-MSDU 802.11n}
 IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT  = $0100; {Mesh Control 802.11s}
 IEEE80211_QOS_CTL_MESH_PS_LEVEL         = $0200; {Mesh Power Save Level}
 IEEE80211_QOS_CTL_RSPI                  = $0400; {Mesh Receiver Service Period Initiated}
 
 {U-APSD queue for WMM IEs sent by AP}
 IEEE80211_WMM_IE_AP_QOSINFO_UAPSD       = (1 shl 7);
 IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK  = $0f;
 {U-APSD queues for WMM IEs sent by STA}
 IEEE80211_WMM_IE_STA_QOSINFO_AC_VO      = (1 shl 0);
 IEEE80211_WMM_IE_STA_QOSINFO_AC_VI      = (1 shl 1);
 IEEE80211_WMM_IE_STA_QOSINFO_AC_BK      = (1 shl 2);
 IEEE80211_WMM_IE_STA_QOSINFO_AC_BE      = (1 shl 3);
 IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK    = $0f;
 
 {U-APSD max SP length for WMM IEs sent by STA}
 IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL     = $00;
 IEEE80211_WMM_IE_STA_QOSINFO_SP_2       = $01;
 IEEE80211_WMM_IE_STA_QOSINFO_SP_4       = $02;
 IEEE80211_WMM_IE_STA_QOSINFO_SP_6       = $03;
 IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK    = $03;
 IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT   = 5;

 IEEE80211_HT_CTL_LEN           = 4;
 
 {Mesh flags} 
 IEEE80211_MESH_FLAGS_AE_A4        = $1;
 IEEE80211_MESH_FLAGS_AE_A5_A6     = $2;
 IEEE80211_MESH_FLAGS_AE           = $3;
 IEEE80211_MESH_FLAGS_PS_DEEP      = $4;
 
 {Mesh PREQ element flags}
 IEEE80211_PREQ_PROACTIVE_PREP_FLAG      = (1 shl 2); {proactive PREP subfield}
 
 {Mesh PREQ element per target flags}
 IEEE80211_PREQ_TO_FLAG  = (1 shl 0); {target only subfield}
 IEEE80211_PREQ_USN_FLAG = (1 shl 2); {unknown target HWMP sequence number subfield}
 
 {Mesh Configuration IE capability field flags}
 IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS          = $01; {STA is willing to establish additional mesh peerings with other mesh STAs}
 IEEE80211_MESHCONF_CAPAB_FORWARDING             = $08; {the STA forwards MSDUs}
 IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING         = $20; {TBTT adjustment procedure is ongoing}
 IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL       = $40; {STA is in deep sleep mode or has neighbors in deep sleep mode}
 
 {Mesh channel switch parameters flags}
 WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT = (1 shl 0);
 WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR   = (1 shl 1);
 WLAN_EID_CHAN_SWITCH_PARAM_REASON      = (1 shl 2); 
 
 {Root announcement flags}
 IEEE80211_RANN_FLAG_IS_GATE = (1 shl 0);
 
 {HT channel width values}
 IEEE80211_HT_CHANWIDTH_20MHZ = 0;
 IEEE80211_HT_CHANWIDTH_ANY   = 1;
 
 {VHT operating mode field bits}
 IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK   = 3;
 IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ  = 0;
 IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ  = 1;
 IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ  = 2;
 IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ = 3;
 IEEE80211_OPMODE_NOTIF_RX_NSS_MASK      = $70;
 IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT     = 4;
 IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF   = $80;
 
 WLAN_SA_QUERY_TR_ID_LEN = 2;
 
 {Supported Rates value encodings in 802.11n-2009 7.3.2.2}
 IEEE80211_BSS_MEMBERSHIP_SELECTOR_HT_PHY = 127;
 
 {Peer-to-Peer IE attribute related definitions}
 IEEE80211_P2P_ATTR_STATUS              = 0;
 IEEE80211_P2P_ATTR_MINOR_REASON        = 1;
 IEEE80211_P2P_ATTR_CAPABILITY          = 2;
 IEEE80211_P2P_ATTR_DEVICE_ID           = 3;
 IEEE80211_P2P_ATTR_GO_INTENT           = 4;
 IEEE80211_P2P_ATTR_GO_CONFIG_TIMEOUT   = 5;
 IEEE80211_P2P_ATTR_LISTEN_CHANNEL      = 6;
 IEEE80211_P2P_ATTR_GROUP_BSSID         = 7;
 IEEE80211_P2P_ATTR_EXT_LISTEN_TIMING   = 8;
 IEEE80211_P2P_ATTR_INTENDED_IFACE_ADDR = 9;
 IEEE80211_P2P_ATTR_MANAGABILITY        = 10;
 IEEE80211_P2P_ATTR_CHANNEL_LIST        = 11;
 IEEE80211_P2P_ATTR_ABSENCE_NOTICE      = 12;
 IEEE80211_P2P_ATTR_DEVICE_INFO         = 13;
 IEEE80211_P2P_ATTR_GROUP_INFO          = 14;
 IEEE80211_P2P_ATTR_GROUP_ID            = 15;
 IEEE80211_P2P_ATTR_INTERFACE           = 16;
 IEEE80211_P2P_ATTR_OPER_CHANNEL        = 17;
 IEEE80211_P2P_ATTR_INVITE_FLAGS        = 18;
 {19 - 220: Reserved}
 IEEE80211_P2P_ATTR_VENDOR_SPECIFIC     = 221;
 
 IEEE80211_P2P_ATTR_MAX  = 222;
 
 IEEE80211_P2P_NOA_DESC_MAX = 4;
 
 IEEE80211_P2P_OPPPS_ENABLE_BIT         = (1 shl 7);
 IEEE80211_P2P_OPPPS_CTWINDOW_MASK      = $7F;

 {802.11 BAR control masks}
 IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL    = $0000;
 IEEE80211_BAR_CTRL_MULTI_TID            = $0002;
 IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA = $0004;
 IEEE80211_BAR_CTRL_TID_INFO_MASK        = $f000;
 IEEE80211_BAR_CTRL_TID_INFO_SHIFT       = 12;
 
 IEEE80211_HT_MCS_MASK_LEN = 10;

 {802.11n HT capability MSC set}
 IEEE80211_HT_MCS_RX_HIGHEST_MASK        = $3ff;
 IEEE80211_HT_MCS_TX_DEFINED             = $01;
 IEEE80211_HT_MCS_TX_RX_DIFF             = $02;
 IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK    = $0C; {value 0 == 1 stream etc}
 IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT   = 2;
 IEEE80211_HT_MCS_TX_MAX_STREAMS         = 4;
 IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION  = $10;
 {802.11n D5.0 20.3.5 / 20.6 says:
  - indices 0 to 7 and 32 are single spatial stream
  - 8 to 31 are multiple spatial streams using equal modulation
    [8..15 for two streams, 16..23 for three and 24..31 for four]
  - remainder are multiple spatial streams using unequal modulation}
 IEEE80211_HT_MCS_UNEQUAL_MODULATION_START = 33;
 IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE = (IEEE80211_HT_MCS_UNEQUAL_MODULATION_START div 8);

 {802.11n HT capabilities masks (for CapabilitiesInfo)}
 IEEE80211_HT_CAP_LDPC_CODING           = $0001;
 IEEE80211_HT_CAP_SUP_WIDTH_20_40       = $0002;
 IEEE80211_HT_CAP_SM_PS                 = $000C;
 IEEE80211_HT_CAP_SM_PS_SHIFT           = 2;
 IEEE80211_HT_CAP_GRN_FLD               = $0010;
 IEEE80211_HT_CAP_SGI_20                = $0020;
 IEEE80211_HT_CAP_SGI_40                = $0040;
 IEEE80211_HT_CAP_TX_STBC               = $0080;
 IEEE80211_HT_CAP_RX_STBC               = $0300;
 IEEE80211_HT_CAP_RX_STBC_SHIFT         = 8;
 IEEE80211_HT_CAP_DELAY_BA              = $0400;
 IEEE80211_HT_CAP_MAX_AMSDU             = $0800;
 IEEE80211_HT_CAP_DSSSCCK40             = $1000;
 IEEE80211_HT_CAP_RESERVED              = $2000;
 IEEE80211_HT_CAP_40MHZ_INTOLERANT      = $4000;
 IEEE80211_HT_CAP_LSIG_TXOP_PROT        = $8000;

 {802.11n HT extended capabilities masks (for ExtendedHTCapInfo)}
 IEEE80211_HT_EXT_CAP_PCO               = $0001;
 IEEE80211_HT_EXT_CAP_PCO_TIME          = $0006;
 IEEE80211_HT_EXT_CAP_PCO_TIME_SHIFT    = 1;
 IEEE80211_HT_EXT_CAP_MCS_FB            = $0300;
 IEEE80211_HT_EXT_CAP_MCS_FB_SHIFT      = 8;
 IEEE80211_HT_EXT_CAP_HTC_SUP           = $0400;
 IEEE80211_HT_EXT_CAP_RD_RESPONDER      = $0800;

 {802.11n HT capability AMPDU settings (for AMPDUParamsInfo)}
 IEEE80211_HT_AMPDU_PARM_FACTOR         = $03;
 IEEE80211_HT_AMPDU_PARM_DENSITY        = $1C;
 IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT  = 2;
 
 {Maximum length of AMPDU that the STA can receive in high-throughput (HT). Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)}
 {As per Linux ieee80211.h ieee80211_max_ampdu_length_exp}
 IEEE80211_HT_MAX_AMPDU_8K  = 0;
 IEEE80211_HT_MAX_AMPDU_16K = 1;
 IEEE80211_HT_MAX_AMPDU_32K = 2;
 IEEE80211_HT_MAX_AMPDU_64K = 3;
 
 {Maximum length of AMPDU that the STA can receive in VHT.Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)}
 {As per Linux ieee80211.h ieee80211_vht_max_ampdu_length_exp}
 IEEE80211_VHT_MAX_AMPDU_8K    = 0;
 IEEE80211_VHT_MAX_AMPDU_16K   = 1;
 IEEE80211_VHT_MAX_AMPDU_32K   = 2;
 IEEE80211_VHT_MAX_AMPDU_64K   = 3;
 IEEE80211_VHT_MAX_AMPDU_128K  = 4;
 IEEE80211_VHT_MAX_AMPDU_256K  = 5;
 IEEE80211_VHT_MAX_AMPDU_512K  = 6;
 IEEE80211_VHT_MAX_AMPDU_1024K = 7;

 IEEE80211_HT_MAX_AMPDU_FACTOR = 13;
 
 {Minimum MPDU start spacing}
 {As per Linux ieee80211.h ieee80211_min_mpdu_spacing}
 IEEE80211_HT_MPDU_DENSITY_NONE = 0;     {No restriction}
 IEEE80211_HT_MPDU_DENSITY_0_25 = 1;     {1/4 usec}
 IEEE80211_HT_MPDU_DENSITY_0_5  = 2;     {1/2 usec}
 IEEE80211_HT_MPDU_DENSITY_1    = 3;     {1 usec}
 IEEE80211_HT_MPDU_DENSITY_2    = 4;     {2 usec}
 IEEE80211_HT_MPDU_DENSITY_4    = 5;     {4 usec}
 IEEE80211_HT_MPDU_DENSITY_8    = 6;     {8 usec}
 IEEE80211_HT_MPDU_DENSITY_16   = 7;     {16 usec} 

 {For HT operation IE HTParam}
 IEEE80211_HT_PARAM_CHA_SEC_OFFSET               = $03;
 IEEE80211_HT_PARAM_CHA_SEC_NONE                 = $00;
 IEEE80211_HT_PARAM_CHA_SEC_ABOVE                = $01;
 IEEE80211_HT_PARAM_CHA_SEC_BELOW                = $03;
 IEEE80211_HT_PARAM_CHAN_WIDTH_ANY               = $04;
 IEEE80211_HT_PARAM_RIFS_MODE                    = $08;
 
 {For HT operation IE OperationMode}
 IEEE80211_HT_OP_MODE_PROTECTION                 = $0003;
 IEEE80211_HT_OP_MODE_PROTECTION_NONE            = 0;
 IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER       = 1;
 IEEE80211_HT_OP_MODE_PROTECTION_20MHZ           = 2;
 IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED     = 3;
 IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT           = $0004;
 IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT           = $0010;
 
 {For HT operation IE STBCParam}
 IEEE80211_HT_STBC_PARAM_DUAL_BEACON             = $0040;
 IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT           = $0080;
 IEEE80211_HT_STBC_PARAM_STBC_BEACON             = $0100;
 IEEE80211_HT_STBC_PARAM_LSIG_TXOP_FULLPROT      = $0200;
 IEEE80211_HT_STBC_PARAM_PCO_ACTIVE              = $0400;
 IEEE80211_HT_STBC_PARAM_PCO_PHASE               = $0800;

 {block-ack parameters}
 IEEE80211_ADDBA_PARAM_AMSDU_MASK     = $0001;
 IEEE80211_ADDBA_PARAM_POLICY_MASK    = $0002;
 IEEE80211_ADDBA_PARAM_TID_MASK       = $003C;
 IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK  = $FFC0;
 IEEE80211_DELBA_PARAM_TID_MASK       = $F000;
 IEEE80211_DELBA_PARAM_INITIATOR_MASK = $0800;
 
 {A-PMDU buffer sizes} {According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2)}
 IEEE80211_MIN_AMPDU_BUF = $8;
 IEEE80211_MAX_AMPDU_BUF = $40;
 
 {Spatial Multiplexing Power Save Modes (for capability)}
 WLAN_HT_CAP_SM_PS_STATIC       = 0;
 WLAN_HT_CAP_SM_PS_DYNAMIC      = 1;
 WLAN_HT_CAP_SM_PS_INVALID      = 2;
 WLAN_HT_CAP_SM_PS_DISABLED     = 3;
 
 {For SM power control field lower two bits}
 WLAN_HT_SMPS_CONTROL_DISABLED  = 0;
 WLAN_HT_SMPS_CONTROL_STATIC    = 1;
 WLAN_HT_SMPS_CONTROL_DYNAMIC   = 3;
 
 {VHT MCS support definitions}
 {These definitions are used in each 2-bit subfield of the RXMCSMap and TXMCSMap fields of TIEEE8021VHTMCSInfo, which are
 both split into 8 subfields by number of streams. These values indicate which MCSes are supported for the number of streams the value appears for}
 IEEE80211_VHT_MCS_SUPPORT_0_7   = 0; {MCSes 0-7 are supported for the number of streams}
 IEEE80211_VHT_MCS_SUPPORT_0_8   = 1; {MCSes 0-8 are supported}
 IEEE80211_VHT_MCS_SUPPORT_0_9   = 2; {MCSes 0-9 are supported}
 IEEE80211_VHT_MCS_NOT_SUPPORTED = 3; {This number of streams isn't supported}
 
 {VHT channel width}
 IEEE80211_VHT_CHANWIDTH_USE_HT          = 0; {use the HT operation IE to determine the channel width (20 or 40 MHz)}
 IEEE80211_VHT_CHANWIDTH_80MHZ           = 1; {80 MHz bandwidth}
 IEEE80211_VHT_CHANWIDTH_160MHZ          = 2; {160 MHz bandwidth}
 IEEE80211_VHT_CHANWIDTH_80P80MHZ        = 3; {80+80 MHz bandwidth}

 {802.11ac VHT Capabilities}
 IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895                  = $00000000;
 IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991                  = $00000001;
 IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454                 = $00000002;
 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ                = $00000004;
 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ       = $00000008;
 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK                  = $0000000C;
 IEEE80211_VHT_CAP_RXLDPC                                = $00000010;
 IEEE80211_VHT_CAP_SHORT_GI_80                           = $00000020;
 IEEE80211_VHT_CAP_SHORT_GI_160                          = $00000040;
 IEEE80211_VHT_CAP_TXSTBC                                = $00000080;
 IEEE80211_VHT_CAP_RXSTBC_1                              = $00000100;
 IEEE80211_VHT_CAP_RXSTBC_2                              = $00000200;
 IEEE80211_VHT_CAP_RXSTBC_3                              = $00000300;
 IEEE80211_VHT_CAP_RXSTBC_4                              = $00000400;
 IEEE80211_VHT_CAP_RXSTBC_MASK                           = $00000700;
 IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE                 = $00000800;
 IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE                 = $00001000;
 IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT                  = 13;
 IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK                   = (7 shl IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT             = 16;
 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK              = (7 shl IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
 IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE                 = $00080000;
 IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE                 = $00100000;
 IEEE80211_VHT_CAP_VHT_TXOP_PS                           = $00200000;
 IEEE80211_VHT_CAP_HTC_VHT                               = $00400000;
 IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT      = 23;
 IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK       = (7 shl IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT);
 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB     = $08000000;
 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB       = $0c000000;
 IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN                    = $10000000;
 IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN                    = $20000000; 
 
 {Authentication algorithms}
 WLAN_AUTH_OPEN       = 0;
 WLAN_AUTH_SHARED_KEY = 1;
 WLAN_AUTH_FT         = 2;
 WLAN_AUTH_SAE        = 3;
 WLAN_AUTH_LEAP       = 128;
 
 WLAN_AUTH_CHALLENGE_LEN = 128;
 
 WLAN_CAPABILITY_ESS     = (1 shl 0);
 WLAN_CAPABILITY_IBSS    = (1 shl 1);
 
 {A mesh STA sets the ESS and IBSS capability bits to zero. however, this holds true for p2p probe responses (in the p2p_find phase) as well.}
 //WLAN_CAPABILITY_IS_STA_BSS(cap) = (!((cap) & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS))) //To DO //ieee80211.h
 WLAN_CAPABILITY_CF_POLLABLE     = (1 shl 2);
 WLAN_CAPABILITY_CF_POLL_REQUEST = (1 shl 3);
 WLAN_CAPABILITY_PRIVACY         = (1 shl 4);
 WLAN_CAPABILITY_SHORT_PREAMBLE  = (1 shl 5);
 WLAN_CAPABILITY_PBCC            = (1 shl 6);
 WLAN_CAPABILITY_CHANNEL_AGILITY = (1 shl 7);
 {802.11h}
 WLAN_CAPABILITY_SPECTRUM_MGMT   = (1 shl 8);
 WLAN_CAPABILITY_QOS             = (1 shl 9);
 WLAN_CAPABILITY_SHORT_SLOT_TIME = (1 shl 10);
 WLAN_CAPABILITY_APSD            = (1 shl 11);
 WLAN_CAPABILITY_RADIO_MEASURE   = (1 shl 12);
 WLAN_CAPABILITY_DSSS_OFDM       = (1 shl 13);
 WLAN_CAPABILITY_DEL_BACK        = (1 shl 14);
 WLAN_CAPABILITY_IMM_BACK        = (1 shl 15);
 
 {DMG (60gHz) 802.11ad}
 {Type - bits 0..1}
 WLAN_CAPABILITY_DMG_TYPE_MASK           = (3 shl 0);
 WLAN_CAPABILITY_DMG_TYPE_IBSS           = (1 shl 0); {Tx by: STA}
 WLAN_CAPABILITY_DMG_TYPE_PBSS           = (2 shl 0); {Tx by: PCP}
 WLAN_CAPABILITY_DMG_TYPE_AP             = (3 shl 0); {Tx by: AP}

 WLAN_CAPABILITY_DMG_CBAP_ONLY           = (1 shl 2);
 WLAN_CAPABILITY_DMG_CBAP_SOURCE         = (1 shl 3);
 WLAN_CAPABILITY_DMG_PRIVACY             = (1 shl 4);
 WLAN_CAPABILITY_DMG_ECPAC               = (1 shl 5);

 WLAN_CAPABILITY_DMG_SPECTRUM_MGMT       = (1 shl 8);
 WLAN_CAPABILITY_DMG_RADIO_MEASURE       = (1 shl 12);
 {Measurement}
 IEEE80211_SPCT_MSR_RPRT_MODE_LATE       = (1 shl 0);
 IEEE80211_SPCT_MSR_RPRT_MODE_INCAPABLE  = (1 shl 1);
 IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED    = (1 shl 2);

 IEEE80211_SPCT_MSR_RPRT_TYPE_BASIC      = 0;
 IEEE80211_SPCT_MSR_RPRT_TYPE_CCA        = 1;
 IEEE80211_SPCT_MSR_RPRT_TYPE_RPI        = 2;
 
 {802.11g ERP information element}
 WLAN_ERP_NON_ERP_PRESENT = (1 shl 0);
 WLAN_ERP_USE_PROTECTION  = (1 shl 1);
 WLAN_ERP_BARKER_PREAMBLE = (1 shl 2);
 
 {WLAN_ERP_BARKER_PREAMBLE values}
 WLAN_ERP_PREAMBLE_SHORT = 0;
 WLAN_ERP_PREAMBLE_LONG = 1;
 
 {Band ID, 802.11ad #8.4.1.45}
 IEEE80211_BANDID_TV_WS = 0; {TV white spaces}
 IEEE80211_BANDID_SUB1  = 1; {Sub-1 GHz (excluding TV white spaces)}
 IEEE80211_BANDID_2G    = 2; {2.4 GHz}
 IEEE80211_BANDID_3G    = 3; {3.6 GHz}
 IEEE80211_BANDID_5G    = 4; {4.9 and 5 GHz}
 IEEE80211_BANDID_60G   = 5; {60 GHz} 

 {802.11 Status codes}
 WLAN_STATUS_SUCCESS = 0;
 WLAN_STATUS_UNSPECIFIED_FAILURE = 1;
 WLAN_STATUS_CAPS_UNSUPPORTED = 10;
 WLAN_STATUS_REASSOC_NO_ASSOC = 11;
 WLAN_STATUS_ASSOC_DENIED_UNSPEC = 12;
 WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG = 13;
 WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION = 14;
 WLAN_STATUS_CHALLENGE_FAIL = 15;
 WLAN_STATUS_AUTH_TIMEOUT = 16;
 WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17;
 WLAN_STATUS_ASSOC_DENIED_RATES = 18;
 {802.11b}
 WLAN_STATUS_ASSOC_DENIED_NOSHORTPREAMBLE = 19;
 WLAN_STATUS_ASSOC_DENIED_NOPBCC = 20;
 WLAN_STATUS_ASSOC_DENIED_NOAGILITY = 21;
 {802.11h}
 WLAN_STATUS_ASSOC_DENIED_NOSPECTRUM = 22;
 WLAN_STATUS_ASSOC_REJECTED_BAD_POWER = 23;
 WLAN_STATUS_ASSOC_REJECTED_BAD_SUPP_CHAN = 24;
 {802.11g}
 WLAN_STATUS_ASSOC_DENIED_NOSHORTTIME = 25;
 WLAN_STATUS_ASSOC_DENIED_NODSSSOFDM = 26;
 {802.11w}
 WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY = 30;
 WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION = 31;
 {802.11i}
 WLAN_STATUS_INVALID_IE = 40;
 WLAN_STATUS_INVALID_GROUP_CIPHER = 41;
 WLAN_STATUS_INVALID_PAIRWISE_CIPHER = 42;
 WLAN_STATUS_INVALID_AKMP = 43;
 WLAN_STATUS_UNSUPP_RSN_VERSION = 44;
 WLAN_STATUS_INVALID_RSN_IE_CAP = 45;
 WLAN_STATUS_CIPHER_SUITE_REJECTED = 46;
 {802.11e}
 WLAN_STATUS_UNSPECIFIED_QOS = 32;
 WLAN_STATUS_ASSOC_DENIED_NOBANDWIDTH = 33;
 WLAN_STATUS_ASSOC_DENIED_LOWACK = 34;
 WLAN_STATUS_ASSOC_DENIED_UNSUPP_QOS = 35;
 WLAN_STATUS_REQUEST_DECLINED = 37;
 WLAN_STATUS_INVALID_QOS_PARAM = 38;
 WLAN_STATUS_CHANGE_TSPEC = 39;
 WLAN_STATUS_WAIT_TS_DELAY = 47;
 WLAN_STATUS_NO_DIRECT_LINK = 48;
 WLAN_STATUS_STA_NOT_PRESENT = 49;
 WLAN_STATUS_STA_NOT_QSTA = 50;
 {802.11s}
 WLAN_STATUS_ANTI_CLOG_REQUIRED = 76;
 WLAN_STATUS_FCG_NOT_SUPP = 78;
 WLAN_STATUS_STA_NO_TBTT = 78;
 {802.11ad}
 WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES = 39;
 WLAN_STATUS_REJECTED_FOR_DELAY_PERIOD = 47;
 WLAN_STATUS_REJECT_WITH_SCHEDULE = 83;
 WLAN_STATUS_PENDING_ADMITTING_FST_SESSION = 86;
 WLAN_STATUS_PERFORMING_FST_NOW = 87;
 WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW = 88;
 WLAN_STATUS_REJECT_U_PID_SETTING = 89;
 WLAN_STATUS_REJECT_DSE_BAND = 96;
 WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL = 99;
 WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT = 103;

 {802.11 Reason codes}
 WLAN_REASON_UNSPECIFIED = 1;
 WLAN_REASON_PREV_AUTH_NOT_VALID = 2;
 WLAN_REASON_DEAUTH_LEAVING = 3;
 WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY = 4;
 WLAN_REASON_DISASSOC_AP_BUSY = 5;
 WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6;
 WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7;
 WLAN_REASON_DISASSOC_STA_HAS_LEFT = 8;
 WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9;
 {802.11h}
 WLAN_REASON_DISASSOC_BAD_POWER = 10;
 WLAN_REASON_DISASSOC_BAD_SUPP_CHAN = 11;
 {802.11i}
 WLAN_REASON_INVALID_IE = 13;
 WLAN_REASON_MIC_FAILURE = 14;
 WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT = 15;
 WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT = 16;
 WLAN_REASON_IE_DIFFERENT = 17;
 WLAN_REASON_INVALID_GROUP_CIPHER = 18;
 WLAN_REASON_INVALID_PAIRWISE_CIPHER = 19;
 WLAN_REASON_INVALID_AKMP = 20;
 WLAN_REASON_UNSUPP_RSN_VERSION = 21;
 WLAN_REASON_INVALID_RSN_IE_CAP = 22;
 WLAN_REASON_IEEE8021X_FAILED = 23;
 WLAN_REASON_CIPHER_SUITE_REJECTED = 24;
 {TDLS (802.11z)}
 WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE = 25;
 WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED = 26;
 {802.11e}
 WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32;
 WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33;
 WLAN_REASON_DISASSOC_LOW_ACK = 34;
 WLAN_REASON_DISASSOC_QAP_EXCEED_TXOP = 35;
 WLAN_REASON_QSTA_LEAVE_QBSS = 36;
 WLAN_REASON_QSTA_NOT_USE = 37;
 WLAN_REASON_QSTA_REQUIRE_SETUP = 38;
 WLAN_REASON_QSTA_TIMEOUT = 39;
 WLAN_REASON_QSTA_CIPHER_NOT_SUPP = 45;
 {802.11s}
 WLAN_REASON_MESH_PEER_CANCELED = 52;
 WLAN_REASON_MESH_MAX_PEERS = 53;
 WLAN_REASON_MESH_CONFIG = 54;
 WLAN_REASON_MESH_CLOSE = 55;
 WLAN_REASON_MESH_MAX_RETRIES = 56;
 WLAN_REASON_MESH_CONFIRM_TIMEOUT = 57;
 WLAN_REASON_MESH_INVALID_GTK = 58;
 WLAN_REASON_MESH_INCONSISTENT_PARAM = 59;
 WLAN_REASON_MESH_INVALID_SECURITY = 60;
 WLAN_REASON_MESH_PATH_ERROR = 61;
 WLAN_REASON_MESH_PATH_NOFORWARD = 62;
 WLAN_REASON_MESH_PATH_DEST_UNREACHABLE = 63;
 WLAN_REASON_MAC_EXISTS_IN_MBSS = 64;
 WLAN_REASON_MESH_CHAN_REGULATORY = 65;
 WLAN_REASON_MESH_CHAN = 66;

 {802.11 Information Element IDs}
 WLAN_EID_SSID = 0;
 WLAN_EID_SUPP_RATES = 1;
 WLAN_EID_FH_PARAMS = 2; {reserved now}
 WLAN_EID_DS_PARAMS = 3;
 WLAN_EID_CF_PARAMS = 4;
 WLAN_EID_TIM = 5;
 WLAN_EID_IBSS_PARAMS = 6;
 WLAN_EID_COUNTRY = 7;
 {8-9 reserved}
 WLAN_EID_REQUEST = 10;
 WLAN_EID_QBSS_LOAD = 11;
 WLAN_EID_EDCA_PARAM_SET = 12;
 WLAN_EID_TSPEC = 13;
 WLAN_EID_TCLAS = 14;
 WLAN_EID_SCHEDULE = 15;
 WLAN_EID_CHALLENGE = 16;
 {17-31 reserved for challenge text extension}
 WLAN_EID_PWR_CONSTRAINT = 32;
 WLAN_EID_PWR_CAPABILITY = 33;
 WLAN_EID_TPC_REQUEST = 34;
 WLAN_EID_TPC_REPORT = 35;
 WLAN_EID_SUPPORTED_CHANNELS = 36;
 WLAN_EID_CHANNEL_SWITCH = 37;
 WLAN_EID_MEASURE_REQUEST = 38;
 WLAN_EID_MEASURE_REPORT = 39;
 WLAN_EID_QUIET = 40;
 WLAN_EID_IBSS_DFS = 41;
 WLAN_EID_ERP_INFO = 42;
 WLAN_EID_TS_DELAY = 43;
 WLAN_EID_TCLAS_PROCESSING = 44;
 WLAN_EID_HT_CAPABILITY = 45;
 WLAN_EID_QOS_CAPA = 46;
 {47 reserved for Broadcom}
 WLAN_EID_RSN = 48;
 WLAN_EID_802_15_COEX = 49;
 WLAN_EID_EXT_SUPP_RATES = 50;
 WLAN_EID_AP_CHAN_REPORT = 51;
 WLAN_EID_NEIGHBOR_REPORT = 52;
 WLAN_EID_RCPI = 53;
 WLAN_EID_MOBILITY_DOMAIN = 54;
 WLAN_EID_FAST_BSS_TRANSITION = 55;
 WLAN_EID_TIMEOUT_INTERVAL = 56;
 WLAN_EID_RIC_DATA = 57;
 WLAN_EID_DSE_REGISTERED_LOCATION = 58;
 WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59;
 WLAN_EID_EXT_CHANSWITCH_ANN = 60;
 WLAN_EID_HT_OPERATION = 61;
 WLAN_EID_SECONDARY_CHANNEL_OFFSET = 62;
 WLAN_EID_BSS_AVG_ACCESS_DELAY = 63;
 WLAN_EID_ANTENNA_INFO = 64;
 WLAN_EID_RSNI = 65;
 WLAN_EID_MEASUREMENT_PILOT_TX_INFO = 66;
 WLAN_EID_BSS_AVAILABLE_CAPACITY = 67;
 WLAN_EID_BSS_AC_ACCESS_DELAY = 68;
 WLAN_EID_TIME_ADVERTISEMENT = 69;
 WLAN_EID_RRM_ENABLED_CAPABILITIES = 70;
 WLAN_EID_MULTIPLE_BSSID = 71;
 WLAN_EID_BSS_COEX_2040 = 72;
 WLAN_EID_BSS_INTOLERANT_CHL_REPORT = 73;
 WLAN_EID_OVERLAP_BSS_SCAN_PARAM = 74;
 WLAN_EID_RIC_DESCRIPTOR = 75;
 WLAN_EID_MMIE = 76;
 WLAN_EID_ASSOC_COMEBACK_TIME = 77;
 WLAN_EID_EVENT_REQUEST = 78;
 WLAN_EID_EVENT_REPORT = 79;
 WLAN_EID_DIAGNOSTIC_REQUEST = 80;
 WLAN_EID_DIAGNOSTIC_REPORT = 81;
 WLAN_EID_LOCATION_PARAMS = 82;
 WLAN_EID_NON_TX_BSSID_CAP =  83;
 WLAN_EID_SSID_LIST = 84;
 WLAN_EID_MULTI_BSSID_IDX = 85;
 WLAN_EID_FMS_DESCRIPTOR = 86;
 WLAN_EID_FMS_REQUEST = 87;
 WLAN_EID_FMS_RESPONSE = 88;
 WLAN_EID_QOS_TRAFFIC_CAPA = 89;
 WLAN_EID_BSS_MAX_IDLE_PERIOD = 90;
 WLAN_EID_TSF_REQUEST = 91;
 WLAN_EID_TSF_RESPOSNE = 92;
 WLAN_EID_WNM_SLEEP_MODE = 93;
 WLAN_EID_TIM_BCAST_REQ = 94;
 WLAN_EID_TIM_BCAST_RESP = 95;
 WLAN_EID_COLL_IF_REPORT = 96;
 WLAN_EID_CHANNEL_USAGE = 97;
 WLAN_EID_TIME_ZONE = 98;
 WLAN_EID_DMS_REQUEST = 99;
 WLAN_EID_DMS_RESPONSE = 100;
 WLAN_EID_LINK_ID = 101;
 WLAN_EID_WAKEUP_SCHEDUL = 102;
 {103 reserved}
 WLAN_EID_CHAN_SWITCH_TIMING = 104;
 WLAN_EID_PTI_CONTROL = 105;
 WLAN_EID_PU_BUFFER_STATUS = 106;
 WLAN_EID_INTERWORKING = 107;
 WLAN_EID_ADVERTISEMENT_PROTOCOL = 108;
 WLAN_EID_EXPEDITED_BW_REQ = 109;
 WLAN_EID_QOS_MAP_SET = 110;
 WLAN_EID_ROAMING_CONSORTIUM = 111;
 WLAN_EID_EMERGENCY_ALERT = 112;
 WLAN_EID_MESH_CONFIG = 113;
 WLAN_EID_MESH_ID = 114;
 WLAN_EID_LINK_METRIC_REPORT = 115;
 WLAN_EID_CONGESTION_NOTIFICATION = 116;
 WLAN_EID_PEER_MGMT = 117;
 WLAN_EID_CHAN_SWITCH_PARAM = 118;
 WLAN_EID_MESH_AWAKE_WINDOW = 119;
 WLAN_EID_BEACON_TIMING = 120;
 WLAN_EID_MCCAOP_SETUP_REQ = 121;
 WLAN_EID_MCCAOP_SETUP_RESP = 122;
 WLAN_EID_MCCAOP_ADVERT = 123;
 WLAN_EID_MCCAOP_TEARDOWN = 124;
 WLAN_EID_GANN = 125;
 WLAN_EID_RANN = 126;
 WLAN_EID_EXT_CAPABILITY = 127;
 {128-129 reserved for Agere}
 WLAN_EID_PREQ = 130;
 WLAN_EID_PREP = 131;
 WLAN_EID_PERR = 132;
 {133-136 reserved for Cisco}
 WLAN_EID_PXU = 137;
 WLAN_EID_PXUC = 138;
 WLAN_EID_AUTH_MESH_PEER_EXCH = 139;
 WLAN_EID_MIC = 140;
 WLAN_EID_DESTINATION_URI = 141;
 WLAN_EID_UAPSD_COEX = 142;
 WLAN_EID_WAKEUP_SCHEDULE = 143;
 WLAN_EID_EXT_SCHEDULE = 144;
 WLAN_EID_STA_AVAILABILITY = 145;
 WLAN_EID_DMG_TSPEC = 146;
 WLAN_EID_DMG_AT = 147;
 WLAN_EID_DMG_CAP = 148;
 {149 reserved for Cisco}
 WLAN_EID_CISCO_VENDOR_SPECIFIC = 150;
 WLAN_EID_DMG_OPERATION = 151;
 WLAN_EID_DMG_BSS_PARAM_CHANGE = 152;
 WLAN_EID_DMG_BEAM_REFINEMENT = 153;
 WLAN_EID_CHANNEL_MEASURE_FEEDBACK = 154;
 {155-156 reserved for Cisco}
 WLAN_EID_AWAKE_WINDOW = 157;
 WLAN_EID_MULTI_BAND = 158;
 WLAN_EID_ADDBA_EXT = 159;
 WLAN_EID_NEXT_PCP_LIST = 160;
 WLAN_EID_PCP_HANDOVER = 161;
 WLAN_EID_DMG_LINK_MARGIN = 162;
 WLAN_EID_SWITCHING_STREAM = 163;
 WLAN_EID_SESSION_TRANSITION = 164;
 WLAN_EID_DYN_TONE_PAIRING_REPORT = 165;
 WLAN_EID_CLUSTER_REPORT = 166;
 WLAN_EID_RELAY_CAP = 167;
 WLAN_EID_RELAY_XFER_PARAM_SET = 168;
 WLAN_EID_BEAM_LINK_MAINT = 169;
 WLAN_EID_MULTIPLE_MAC_ADDR = 170;
 WLAN_EID_U_PID = 171;
 WLAN_EID_DMG_LINK_ADAPT_ACK = 172;
 {173 reserved for Symbol}
 WLAN_EID_MCCAOP_ADV_OVERVIEW = 174;
 WLAN_EID_QUIET_PERIOD_REQ = 175;
 {176 reserved for Symbol}
 WLAN_EID_QUIET_PERIOD_RESP = 177;
 {178-179 reserved for Symbol}
 {180 reserved for ISO/IEC 20011}
 WLAN_EID_EPAC_POLICY = 182;
 WLAN_EID_CLISTER_TIME_OFF = 183;
 WLAN_EID_INTER_AC_PRIO = 184;
 WLAN_EID_SCS_DESCRIPTOR = 185;
 WLAN_EID_QLOAD_REPORT = 186;
 WLAN_EID_HCCA_TXOP_UPDATE_COUNT = 187;
 WLAN_EID_HL_STREAM_ID = 188;
 WLAN_EID_GCR_GROUP_ADDR = 189;
 WLAN_EID_ANTENNA_SECTOR_ID_PATTERN = 190;
 WLAN_EID_VHT_CAPABILITY = 191;
 WLAN_EID_VHT_OPERATION = 192;
 WLAN_EID_EXTENDED_BSS_LOAD = 193;
 WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194;
 WLAN_EID_VHT_TX_POWER_ENVELOPE = 195;
 WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196;
 WLAN_EID_AID = 197;
 WLAN_EID_QUIET_CHANNEL = 198;
 WLAN_EID_OPMODE_NOTIF = 199;
 
 WLAN_EID_VENDOR_SPECIFIC = 221;
 WLAN_EID_QOS_PARAMETER = 222; 
 
 {802.11 Action category code}
 WLAN_CATEGORY_SPECTRUM_MGMT = 0;
 WLAN_CATEGORY_QOS = 1;
 WLAN_CATEGORY_DLS = 2;
 WLAN_CATEGORY_BACK = 3;
 WLAN_CATEGORY_PUBLIC = 4;
 WLAN_CATEGORY_RADIO_MEASUREMENT = 5;
 WLAN_CATEGORY_HT = 7;
 WLAN_CATEGORY_SA_QUERY = 8;
 WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION = 9;
 WLAN_CATEGORY_WNM = 10;
 WLAN_CATEGORY_WNM_UNPROTECTED = 11;
 WLAN_CATEGORY_TDLS = 12;
 WLAN_CATEGORY_MESH_ACTION = 13;
 WLAN_CATEGORY_MULTIHOP_ACTION = 14;
 WLAN_CATEGORY_SELF_PROTECTED = 15;
 WLAN_CATEGORY_DMG = 16;
 WLAN_CATEGORY_WMM = 17;
 WLAN_CATEGORY_FST = 18;
 WLAN_CATEGORY_UNPROT_DMG = 20;
 WLAN_CATEGORY_VHT = 21;
 WLAN_CATEGORY_VENDOR_SPECIFIC_PROTECTED = 126;
 WLAN_CATEGORY_VENDOR_SPECIFIC = 127; 
 
 {SPECTRUM_MGMT action code}
 WLAN_ACTION_SPCT_MSR_REQ = 0;
 WLAN_ACTION_SPCT_MSR_RPRT = 1;
 WLAN_ACTION_SPCT_TPC_REQ = 2;
 WLAN_ACTION_SPCT_TPC_RPRT = 3;
 WLAN_ACTION_SPCT_CHL_SWITCH = 4; 
 
 {HT action codes}
 WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0;
 WLAN_HT_ACTION_SMPS = 1;
 WLAN_HT_ACTION_PSMP = 2;
 WLAN_HT_ACTION_PCO_PHASE = 3;
 WLAN_HT_ACTION_CSI = 4;
 WLAN_HT_ACTION_NONCOMPRESSED_BF = 5;
 WLAN_HT_ACTION_COMPRESSED_BF = 6;
 WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7; 

 {VHT action codes}
 WLAN_VHT_ACTION_COMPRESSED_BF = 0;
 WLAN_VHT_ACTION_GROUPID_MGMT = 1;
 WLAN_VHT_ACTION_OPMODE_NOTIF = 2;

 {Self Protected Action codes}
 WLAN_SP_RESERVED = 0;
 WLAN_SP_MESH_PEERING_OPEN = 1;
 WLAN_SP_MESH_PEERING_CONFIRM = 2;
 WLAN_SP_MESH_PEERING_CLOSE = 3;
 WLAN_SP_MGK_INFORM = 4;
 WLAN_SP_MGK_ACK = 5;

 {Mesh action codes}
 WLAN_MESH_ACTION_LINK_METRIC_REPORT = 0;
 WLAN_MESH_ACTION_HWMP_PATH_SELECTION = 1;
 WLAN_MESH_ACTION_GATE_ANNOUNCEMENT = 2;
 WLAN_MESH_ACTION_CONGESTION_CONTROL_NOTIFICATION = 3;
 WLAN_MESH_ACTION_MCCA_SETUP_REQUEST = 4;
 WLAN_MESH_ACTION_MCCA_SETUP_REPLY = 5;
 WLAN_MESH_ACTION_MCCA_ADVERTISEMENT_REQUEST = 6;
 WLAN_MESH_ACTION_MCCA_ADVERTISEMENT = 7;
 WLAN_MESH_ACTION_MCCA_TEARDOWN = 8;
 WLAN_MESH_ACTION_TBTT_ADJUSTMENT_REQUEST = 9;
 WLAN_MESH_ACTION_TBTT_ADJUSTMENT_RESPONSE = 10;

 {Security key length}
 WLAN_KEY_LEN_WEP40 = 5;
 WLAN_KEY_LEN_WEP104 = 13;
 WLAN_KEY_LEN_CCMP = 16;
 WLAN_KEY_LEN_CCMP_256 = 32;
 WLAN_KEY_LEN_TKIP = 32;
 WLAN_KEY_LEN_AES_CMAC = 16;
 WLAN_KEY_LEN_SMS4 = 32;
 WLAN_KEY_LEN_GCMP = 16;
 WLAN_KEY_LEN_GCMP_256 = 32;
 WLAN_KEY_LEN_BIP_CMAC_256 = 32;
 WLAN_KEY_LEN_BIP_GMAC_128 = 16;
 WLAN_KEY_LEN_BIP_GMAC_256 = 32;

 IEEE80211_WEP_IV_LEN           = 4;
 IEEE80211_WEP_ICV_LEN          = 4;
 IEEE80211_CCMP_HDR_LEN         = 8;
 IEEE80211_CCMP_MIC_LEN         = 8;
 IEEE80211_CCMP_PN_LEN          = 6;
 IEEE80211_CCMP_256_HDR_LEN     = 8;
 IEEE80211_CCMP_256_MIC_LEN     = 16;
 IEEE80211_CCMP_256_PN_LEN      = 6;
 IEEE80211_TKIP_IV_LEN          = 8;
 IEEE80211_TKIP_ICV_LEN         = 4;
 IEEE80211_CMAC_PN_LEN          = 6;
 IEEE80211_GMAC_PN_LEN          = 6;
 IEEE80211_GCMP_HDR_LEN         = 8;
 IEEE80211_GCMP_MIC_LEN         = 16;
 IEEE80211_GCMP_PN_LEN          = 6;

 {Public action codes}
 WLAN_PUB_ACTION_EXT_CHANSW_ANN = 4;
 WLAN_PUB_ACTION_TDLS_DISCOVER_RES = 14;

 {TDLS action codes}
 WLAN_TDLS_SETUP_REQUEST = 0;
 WLAN_TDLS_SETUP_RESPONSE = 1;
 WLAN_TDLS_SETUP_CONFIRM = 2;
 WLAN_TDLS_TEARDOWN = 3;
 WLAN_TDLS_PEER_TRAFFIC_INDICATION = 4;
 WLAN_TDLS_CHANNEL_SWITCH_REQUEST = 5;
 WLAN_TDLS_CHANNEL_SWITCH_RESPONSE = 6;
 WLAN_TDLS_PEER_PSM_REQUEST = 7;
 WLAN_TDLS_PEER_PSM_RESPONSE = 8;
 WLAN_TDLS_PEER_TRAFFIC_RESPONSE = 9;
 WLAN_TDLS_DISCOVERY_REQUEST = 10;

 {Extended Channel Switching capability to be set in the 1st byte of the WLAN_EID_EXT_CAPABILITY information element}
 WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING    = (1 shl 2);

 {TDLS capabilities in the 4th byte of WLAN_EID_EXT_CAPABILITY}
 WLAN_EXT_CAPA4_TDLS_BUFFER_STA          = (1 shl 4);
 WLAN_EXT_CAPA4_TDLS_PEER_PSM            = (1 shl 5);
 WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH         = (1 shl 6);

 {Interworking capabilities are set in 7th bit of 4th byte of the WLAN_EID_EXT_CAPABILITY information element}
 WLAN_EXT_CAPA4_INTERWORKING_ENABLED     = (1 shl 7);

 {TDLS capabililites to be enabled in the 5th byte of the WLAN_EID_EXT_CAPABILITY information element}
 WLAN_EXT_CAPA5_TDLS_ENABLED     = (1 shl 5);
 WLAN_EXT_CAPA5_TDLS_PROHIBITED  = (1 shl 6);
 WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED    = (1 shl 7);

 WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED     = (1 shl 5);
 WLAN_EXT_CAPA8_OPMODE_NOTIF     = (1 shl 6);

 {TDLS specific payload type in the LLC/SNAP header}
 WLAN_TDLS_SNAP_RFTYPE   = $2;
 
 {BSS Coex IE information field bits}
 WLAN_BSS_COEX_INFORMATION_REQUEST       = (1 shl 0);

 {mesh synchronization method identifier}
 IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET = 1;   {The default synchronization method}
 IEEE80211_SYNC_METHOD_VENDOR          = 255; {A vendor specific synchronization method that will be specified in a vendor specific information element}
 
 {mesh path selection protocol identifier}
 IEEE80211_PATH_PROTOCOL_HWMP   = 1;   {The default path selection protocol}
 IEEE80211_PATH_PROTOCOL_VENDOR = 255; {A vendor specific protocol that will be specified in a vendor specific information element}
 
 {mesh path selection metric identifier}
 IEEE80211_PATH_METRIC_AIRTIME = 1;   {the default path selection metric}
 IEEE80211_PATH_METRIC_VENDOR  = 255; {a vendor specific metric that will be specified in a vendor specific information element}
 
 {root mesh STA mode identifier} {These attribute are used by dot11MeshHWMPRootMode to set root mesh STA mode}
 IEEE80211_ROOTMODE_NO_ROOT         = 0; {the mesh STA is not a root mesh STA (default)}
 IEEE80211_ROOTMODE_ROOT            = 1; {the mesh STA is a root mesh STA if greater than this value}
 IEEE80211_PROACTIVE_PREQ_NO_PREP   = 2; {the mesh STA is a root mesh STA supports the proactive PREQ with proactive PREP subfield set to 0}
 IEEE80211_PROACTIVE_PREQ_WITH_PREP = 3; {the mesh STA is a root mesh STA supports the proactive PREQ with proactive PREP subfield set to 1}
 IEEE80211_PROACTIVE_RANN           = 4; {the mesh STA is a root mesh STA supports the proactive RANN}

 {IEEE 802.11-2007 7.3.2.9 Country information element} {Minimum length is 8 octets, ie len must be evenly divisible by 2}
 IEEE80211_COUNTRY_IE_MIN_LEN   = 6;
 {The Country String field of the element shall be 3 octets in length}
 IEEE80211_COUNTRY_STRING_LEN   = 3;
 
 {For regulatory extension stuff see IEEE 802.11-2007 Annex I (page 1141) and Annex J (page 1147). Also review 7.3.2.9.}
 {When dot11RegulatoryClassesRequired is true and the
  first_channel/reg_extension_id is >= 201 then the IE
  compromises of the 'ext' struct represented below:
 
   - Regulatory extension ID - when generating IE this just needs
     to be monotonically increasing for each triplet passed in
     the IE
   - Regulatory class - index into set of rules
   - Coverage class - index into air propagation time (Table 7-27),
     in microseconds, you can compute the air propagation time from
     the index by multiplying by 3, so index 10 yields a propagation
     of 10 us. Valid values are 0-31, values 32-255 are not defined
     yet. A value of 0 inicates air propagation of <= 1 us.
 
   See also Table I.2 for Emission limit sets and table
   I.3 for Behavior limit sets. Table J.1 indicates how to map
   a reg_class to an emission limit set and behavior limit set}
 IEEE80211_COUNTRY_EXTENSION_ID = 201;

 {Timeout Interval}
 WLAN_TIMEOUT_REASSOC_DEADLINE = 1; {802.11r}
 WLAN_TIMEOUT_KEY_LIFETIME = 2; {802.11r}
 WLAN_TIMEOUT_ASSOC_COMEBACK = 3; {802.11w} 

 {BACK action code}
 WLAN_ACTION_ADDBA_REQ = 0;
 WLAN_ACTION_ADDBA_RESP = 1;
 WLAN_ACTION_DELBA = 2; 
 
 {BACK (block-ack) parties}
 WLAN_BACK_RECIPIENT = 0;
 WLAN_BACK_INITIATOR = 1; 

 {SA Query action}
 WLAN_ACTION_SA_QUERY_REQUEST = 0;
 WLAN_ACTION_SA_QUERY_RESPONSE = 1; 

 {cipher suite selectors}
 WLAN_CIPHER_SUITE_USE_GROUP     = $000FAC00;
 WLAN_CIPHER_SUITE_WEP40         = $000FAC01;
 WLAN_CIPHER_SUITE_TKIP          = $000FAC02;
 {reserved:                      = $000FAC03}
 WLAN_CIPHER_SUITE_CCMP          = $000FAC04;
 WLAN_CIPHER_SUITE_WEP104        = $000FAC05;
 WLAN_CIPHER_SUITE_AES_CMAC      = $000FAC06;
 WLAN_CIPHER_SUITE_GCMP          = $000FAC08;
 WLAN_CIPHER_SUITE_GCMP_256      = $000FAC09;
 WLAN_CIPHER_SUITE_CCMP_256      = $000FAC0A;
 WLAN_CIPHER_SUITE_BIP_GMAC_128  = $000FAC0B;
 WLAN_CIPHER_SUITE_BIP_GMAC_256  = $000FAC0C;
 WLAN_CIPHER_SUITE_BIP_CMAC_256  = $000FAC0D;

 WLAN_CIPHER_SUITE_SMS4          = $00147201;
 
 {AKM suite selectors}
 WLAN_AKM_SUITE_8021X            = $000FAC01;
 WLAN_AKM_SUITE_PSK              = $000FAC02;
 WLAN_AKM_SUITE_8021X_SHA256     = $000FAC05;
 WLAN_AKM_SUITE_PSK_SHA256       = $000FAC06;
 WLAN_AKM_SUITE_TDLS             = $000FAC07;
 WLAN_AKM_SUITE_SAE              = $000FAC08;
 WLAN_AKM_SUITE_FT_OVER_SAE      = $000FAC09;

 WLAN_MAX_KEY_LEN                = 32;

 WLAN_PMKID_LEN                  = 16;

 WLAN_OUI_WFA                    = $506f9a;
 WLAN_OUI_TYPE_WFA_P2P           = 9;
 WLAN_OUI_MICROSOFT              = $0050f2;
 WLAN_OUI_TYPE_MICROSOFT_WPA     = 1;
 WLAN_OUI_TYPE_MICROSOFT_WMM     = 2;
 WLAN_OUI_TYPE_MICROSOFT_WPS     = 4;

 {WMM/802.11e Tspec Element}
 IEEE80211_WMM_IE_TSPEC_TID_MASK         = $0F;
 IEEE80211_WMM_IE_TSPEC_TID_SHIFT        = 1;

 IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED = 0;
 IEEE80211_TSPEC_STATUS_ADDTS_INVAL_PARAMS = $1;
 
 {These constants are NOT defined by IEEE80211 but are internal to the implementation}
 {IEEE80211 frequency bands} {As per Linux cfg80211.h IEEE80211_BAND_*}
 IEEE80211_BAND_2GHZ  = 0; {NL80211_BAND_2GHZ}  {2.4GHz ISM band}
 IEEE80211_BAND_5GHZ  = 1; {NL80211_BAND_5GHZ}  {5GHz band (4.9-5.7)}
 IEEE80211_BAND_60GHZ = 2; {NL80211_BAND_60GHZ} {60 GHz band (58.32 - 64.80 GHz)}
 
 IEEE80211_NUM_BANDS  = 3;

 {IEEE80211 channel flags} {As per Linux cfg80211.h IEEE80211_CHAN_*}
 IEEE80211_CHAN_DISABLED      = (1 shl 0); {This channel is disabled}
 IEEE80211_CHAN_NO_IR         = (1 shl 1); {Do not initiate radiation, this includes sending probe requests or beaconing}
 {(1 shl 2)}
 IEEE80211_CHAN_RADAR         = (1 shl 3); {Radar detection is required on this channel}
 IEEE80211_CHAN_NO_HT40PLUS   = (1 shl 4); {Extension channel above this channel is not permitted}
 IEEE80211_CHAN_NO_HT40MINUS  = (1 shl 5); {Extension channel below this channel is not permitted}
 IEEE80211_CHAN_NO_OFDM       = (1 shl 6); {OFDM is not allowed on this channel}
 IEEE80211_CHAN_NO_80MHZ      = (1 shl 7); {If the driver supports 80 MHz on the band, this flag indicates that an 80 MHz channel cannot use this channel as the control or any of the secondary channels}
 IEEE80211_CHAN_NO_160MHZ     = (1 shl 8); {If the driver supports 160 MHz on the band,	this flag indicates that an 160 MHz channel cannot use this	channel as the control or any of the secondary channels}
 IEEE80211_CHAN_INDOOR_ONLY   = (1 shl 9); {see NL80211_FREQUENCY_ATTR_INDOOR_ONLY}
 IEEE80211_CHAN_GO_CONCURRENT = (1 shl 10); {see NL80211_FREQUENCY_ATTR_GO_CONCURRENT}
 IEEE80211_CHAN_NO_20MHZ      = (1 shl 11); {20 MHz bandwidth is not permitted on this channel}
 IEEE80211_CHAN_NO_10MHZ      = (1 shl 12); {10 MHz bandwidth is not permitted on this channel}

 IEEE80211_CHAN_NO_HT40       = IEEE80211_CHAN_NO_HT40PLUS or IEEE80211_CHAN_NO_HT40MINUS;
 
 
 IEEE80211_DFS_MIN_CAC_TIME_MS = 60000;
 IEEE80211_DFS_MIN_NOP_TIME_MS = (30 * 60 * 1000);
 
 {IEEE80211 rate flags} {As per Linux cfg80211.h IEEE80211_RATE_*}
 IEEE80211_RATE_SHORT_PREAMBLE = (1 shl 0);
 IEEE80211_RATE_MANDATORY_A    = (1 shl 1);
 IEEE80211_RATE_MANDATORY_B    = (1 shl 2);
 IEEE80211_RATE_MANDATORY_G    = (1 shl 3);
 IEEE80211_RATE_ERP_G          = (1 shl 4);
 IEEE80211_RATE_SUPPORTS_5MHZ  = (1 shl 5);
 IEEE80211_RATE_SUPPORTS_10MHZ = (1 shl 6);
 
 {IEEE80211 type values} {As per Linux cfg80211.h IEEE80211_BSS_TYPE_*}
 IEEE80211_BSS_TYPE_ESS  = 0; {Infrastructure BSS}
 IEEE80211_BSS_TYPE_PBSS = 1; {Personal BSS}
 IEEE80211_BSS_TYPE_IBSS = 2; {Independent BSS}
 IEEE80211_BSS_TYPE_MBSS = 3; {Mesh BSS}
 IEEE80211_BSS_TYPE_ANY  = 4; {Wildcard value for matching any BSS type}
 
 {IEEE80211 privacy values} {As per Linux cfg80211.h IEEE80211_PRIVACY_*}
 IEEE80211_PRIVACY_ON  = 0; {privacy bit set}
 IEEE80211_PRIVACY_OFF = 1; {privacy bit clear}
 IEEE80211_PRIVACY_ANY = 2; {Wildcard value for matching any privacy setting}
 
 //To Do //IEEE80211_PRIVACY macro
 
 {IEEE80211 filter flags} {As per Linux mac80211.h FIF_*}
 IEEE80211_FIF_PROMISC_IN_BSS      = (1 shl 0); {promiscuous mode within your BSS, think of the BSS as your network segment and then this corresponds to the regular ethernet device promiscuous mode}
 IEEE80211_FIF_ALLMULTI            = (1 shl 1); {pass all multicast frames, this is used if requested by the user or if the hardware is not capable of filtering by multicast address}
 IEEE80211_FIF_FCSFAIL             = (1 shl 2); {pass frames with failed FCS}
 IEEE80211_FIF_PLCPFAIL            = (1 shl 3); {pass frames with failed PLCP CRC}
 IEEE80211_FIF_BCN_PRBRESP_PROMISC = (1 shl 4); {set during scanning to indicate to the hardware that it should not filter beacons or probe responses by BSSID}
 IEEE80211_FIF_CONTROL             = (1 shl 5); {pass control frames (except for PS Poll), if PROMISC_IN_BSS is not set then only those addressed to this station}
 IEEE80211_FIF_OTHER_BSS           = (1 shl 6); {pass frames destined to other BSSes}
 IEEE80211_FIF_PSPOLL              = (1 shl 7); {pass PS Poll frames, if PROMISC_IN_BSS is not set then only those addressed to this station}
 IEEE80211_FIF_PROBE_REQ           = (1 shl 8); {pass probe request frames}

 {IEEE80211 hardware flags} {As per Linux mac80211.h IEEE80211_HW_*}
 IEEE80211_HW_HAS_RATE_CONTROL              = (1 shl 0);
 IEEE80211_HW_RX_INCLUDES_FCS               = (1 shl 1);  {Indicates that received frames passed to the stack include the FCS at the end}
 IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING   = (1 shl 2);
 IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE     = (1 shl 3);  {Hardware is not capable of short slot operation on the 2.4 GHz band}
 IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE = (1 shl 4);  {Hardware is not capable of receiving frames with short preamble on the 2.4 GHz band}
 IEEE80211_HW_SIGNAL_UNSPEC                 = (1 shl 5);  {Hardware can provide signal values but we don't know its units. We expect values between 0 and MaxSignal}
 IEEE80211_HW_SIGNAL_DBM                    = (1 shl 6);
 IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC        = (1 shl 7);
 IEEE80211_HW_SPECTRUM_MGMT                 = (1 shl 8);
 IEEE80211_HW_AMPDU_AGGREGATION             = (1 shl 9);
 IEEE80211_HW_SUPPORTS_PS                   = (1 shl 10);
 IEEE80211_HW_PS_NULLFUNC_STACK             = (1 shl 11);
 IEEE80211_HW_SUPPORTS_DYNAMIC_PS           = (1 shl 12);
 IEEE80211_HW_MFP_CAPABLE                   = (1 shl 13);
 IEEE80211_HW_WANT_MONITOR_VIF              = (1 shl 14);
 IEEE80211_HW_NO_AUTO_VIF                   = (1 shl 15);
 IEEE80211_HW_SW_CRYPTO_CONTROL             = (1 shl 16);
 {free slots}                               
 IEEE80211_HW_REPORTS_TX_ACK_STATUS         = (1 shl 18);
 IEEE80211_HW_CONNECTION_MONITOR            = (1 shl 19);
 IEEE80211_HW_QUEUE_CONTROL                 = (1 shl 20);
 IEEE80211_HW_SUPPORTS_PER_STA_GTK          = (1 shl 21);
 IEEE80211_HW_AP_LINK_PS                    = (1 shl 22);
 IEEE80211_HW_TX_AMPDU_SETUP_IN_HW          = (1 shl 23);
 IEEE80211_HW_SUPPORTS_RC_TABLE             = (1 shl 24);
 IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF         = (1 shl 25);
 IEEE80211_HW_TIMING_BEACON_ONLY            = (1 shl 26);
 IEEE80211_HW_SUPPORTS_HT_CCK_RATES         = (1 shl 27);
 IEEE80211_HW_CHANCTX_STA_CSA               = (1 shl 28);
 IEEE80211_HW_SUPPORTS_CLONED_SKBS          = (1 shl 29);
 IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS      = (1 shl 30);
 
 {IEEE80211 configuration flags} {As per Linux mac80211.h IEEE80211_CONF_*}
 IEEE80211_CONF_MONITOR    = (1 shl 0);
 IEEE80211_CONF_PS         = (1 shl 1);
 IEEE80211_CONF_IDLE       = (1 shl 2);
 IEEE80211_CONF_OFFCHANNEL = (1 shl 3);
 
 {IEEE80211 configuration changed flags} {As per Linux mac80211.h IEEE80211_CONF_CHANGE_*}
 IEEE80211_CONF_CHANGE_SMPS            = (1 shl 1);
 IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = (1 shl 2);
 IEEE80211_CONF_CHANGE_MONITOR         = (1 shl 3);
 IEEE80211_CONF_CHANGE_PS              = (1 shl 4);
 IEEE80211_CONF_CHANGE_POWER           = (1 shl 5);
 IEEE80211_CONF_CHANGE_CHANNEL         = (1 shl 6);
 IEEE80211_CONF_CHANGE_RETRY_LIMITS    = (1 shl 7);
 IEEE80211_CONF_CHANGE_IDLE            = (1 shl 8);
 
 IEEE80211_TX_STATUS_HEADROOM = 14;

 IEEE80211_MAX_CSA_COUNTERS_NUM = 2;
 
 //To Do 
 
{==============================================================================}
const
 {WiFi specific constants}
 WIFI_DEVICE_TIMER_INTERVAL = 500; {Timer interval for new device additions}
 
 EAPOL_TRANSPORT_NAME = 'EAPOL';
 RSN_TRANSPORT_NAME = 'RSN';

 {WiFi Device States}
 WIFI_STATE_NONE = 0;
 //To Do
 
 {WiFi Device Status}
 WIFI_STATUS_NONE = 0;
 //To Do
 
 {WiFi Device Flags} {As per Linux cfg80211.h WIPHY_FLAG_*}
 {(1 shl 0)}
 {(1 shl 1)}
 {(1 shl 2)}
 WIFI_FLAG_NETNS_OK              = (1 shl 3);  {if not set, do not allow changing the netns of this wiphy at all}
 WIFI_FLAG_PS_ON_BY_DEFAULT      = (1 shl 4);  {if set to true, powersave will be enabled by default}
 WIFI_FLAG_4ADDR_AP              = (1 shl 5);  {supports 4addr mode even on AP (with a single station on a VLAN interface)}
 WIFI_FLAG_4ADDR_STATION         = (1 shl 6);  {supports 4addr mode even as a station}
 WIFI_FLAG_CONTROL_PORT_PROTOCOL = (1 shl 7);  {This device supports setting the control port protocol ethertype}
 WIFI_FLAG_IBSS_RSN              = (1 shl 8);  {The device supports IBSS RSN}
 WIFI_FLAG_MESH_AUTH             = (1 shl 10); {The device supports mesh authentication}
 WIFI_FLAG_SUPPORTS_SCHED_SCAN   = (1 shl 11); {The device supports scheduled scans}
 {(1 shl 12)}
 WIFI_FLAG_SUPPORTS_FW_ROAM      = (1 shl 13); {The device supports roaming feature in the firmware}
 WIFI_FLAG_AP_UAPSD              = (1 shl 14); {The device supports uapsd on AP}
 WIFI_FLAG_SUPPORTS_TDLS         = (1 shl 15); {The device supports TDLS (802.11z) operation}
 WIFI_FLAG_TDLS_EXTERNAL_SETUP   = (1 shl 16); {The device does not handle TDLS (802.11z) link setup/discovery operations internally}
 WIFI_FLAG_HAVE_AP_SME           = (1 shl 17); {device integrates AP SME}
 WIFI_FLAG_REPORTS_OBSS          = (1 shl 18); {the device will report beacons from other BSSes}
 WIFI_FLAG_AP_PROBE_RESP_OFFLOAD = (1 shl 19); {When operating as an AP, the device responds to probe-requests in hardware}
 WIFI_FLAG_OFFCHAN_TX            = (1 shl 20); {Device supports direct off-channel TX}
 WIFI_FLAG_HAS_REMAIN_ON_CHANNEL = (1 shl 21); {Device supports remain-on-channel call}
 WIFI_FLAG_SUPPORTS_5_10_MHZ     = (1 shl 22); {Device supports 5 MHz and 10 MHz channels}
 WIFI_FLAG_HAS_CHANNEL_SWITCH    = (1 shl 23); {Device supports channel switch in beaconing mode (AP, IBSS, Mesh, ...)}
 
 {WiFi Device Features} {As per Linux nl80211.h NL80211_FEATURE_*}
 WIFI_FEATURE_SK_TX_STATUS               = (1 shl 0); {This driver supports reflecting back TX status to the socket error queue when requested with the socket option}
 WIFI_FEATURE_HT_IBSS                    = (1 shl 1); {This driver supports IBSS with HT datarates}
 WIFI_FEATURE_INACTIVITY_TIMER           = (1 shl 2); {This driver takes care of freeing up the connected inactive stations in AP mode}
 WIFI_FEATURE_CELL_BASE_REG_HINTS        = (1 shl 3); {This driver has been tested to work properly to support receiving regulatory hints from cellular base stations}
 WIFI_FEATURE_P2P_DEVICE_NEEDS_CHANNEL   = (1 shl 4); {(no longer available, only here to reserve the value for API/ABI compatibility)}
 WIFI_FEATURE_SAE                        = (1 shl 5); {This driver supports simultaneous authentication of equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station mode}
 WIFI_FEATURE_LOW_PRIORITY_SCAN          = (1 shl 6); {This driver supports low priority scan}
 WIFI_FEATURE_SCAN_FLUSH                 = (1 shl 7); {Scan flush is supported}
 WIFI_FEATURE_AP_SCAN                    = (1 shl 8); {Support scanning using an AP vif}
 WIFI_FEATURE_VIF_TXPOWER                = (1 shl 9); {The driver supports per-vif TX power setting}
 WIFI_FEATURE_NEED_OBSS_SCAN             = (1 shl 10); {The driver expects userspace to perform OBSS scans and generate 20/40 BSS coex reports}
 WIFI_FEATURE_P2P_GO_CTWIN               = (1 shl 11); {P2P GO implementation supports CT Window setting}
 WIFI_FEATURE_P2P_GO_OPPPS               = (1 shl 12); {P2P GO implementation supports opportunistic powersave}
 {bit 13 is reserved}                    
 WIFI_FEATURE_ADVERTISE_CHAN_LIMITS      = (1 shl 14); {cfg80211 advertises channel limits (HT40, VHT 80/160 MHz) if this flag is set}
 WIFI_FEATURE_FULL_AP_CLIENT_STATE       = (1 shl 15); {The driver supports full state transitions for AP clients}
 WIFI_FEATURE_USERSPACE_MPM              = (1 shl 16); {This driver supports a userspace Mesh Peering Management}
 WIFI_FEATURE_ACTIVE_MONITOR             = (1 shl 17); {This driver supports an active monitor interface}
 WIFI_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE  = (1 shl 18); {This driver supports dynamic channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the lifetime of a BSS}
 WIFI_FEATURE_DS_PARAM_SET_IE_IN_PROBES  = (1 shl 19); {This device adds a DS Parameter Set IE to probe requests}
 WIFI_FEATURE_WFA_TPC_IE_IN_PROBES       = (1 shl 20); {This device adds a WFA TPC Report IE to probe requests}
 WIFI_FEATURE_QUIET                      = (1 shl 21); {This device, in client mode, supports Quiet Period requests sent to it by an AP}
 WIFI_FEATURE_TX_POWER_INSERTION         = (1 shl 22); {This device is capable of inserting the current tx power value into the TPC Report IE in the spectrum management TPC Report action frame, and in the Radio Measurement Link Measurement Report action frame}
 WIFI_FEATURE_ACKTO_ESTIMATION           = (1 shl 23); {This driver supports dynamic ACK timeout estimation}
 WIFI_FEATURE_STATIC_SMPS                = (1 shl 24); {Device supports static spatial multiplexing powersave}
 WIFI_FEATURE_DYNAMIC_SMPS               = (1 shl 25); {Device supports dynamic spatial multiplexing powersave}
 WIFI_FEATURE_SUPPORTS_WMM_ADMISSION     = (1 shl 26); {The device supports setting up WMM TSPEC sessions}
 WIFI_FEATURE_MAC_ON_CREATE              = (1 shl 27); {Device supports configuring the vif's MAC address upon creation}
 WIFI_FEATURE_TDLS_CHANNEL_SWITCH        = (1 shl 28); {Driver supports channel switching when operating as a TDLS peer}
 WIFI_FEATURE_SCAN_RANDOM_MAC_ADDR       = (1 shl 29); {This device/driver supports using a random MAC address during scan (if the device is unassociated)}
 WIFI_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR = (1 shl 30); {This device/driver supports using a random MAC address for every scan iteration during scheduled scan (while not associated)}
 WIFI_FEATURE_ND_RANDOM_MAC_ADDR         = (1 shl 31); {This device/driver supports using a random MAC address for every scan iteration during "net detect", i.e. scan in unassociated WoWLAN}
 
 {WiFi Device LED values}
 WIFI_LED_OFF  = 0;
 WIFI_LED_HALF = 127;
 WIFI_LED_FULL = 255;
 
 {WiFi Interface Types} {As per Linux nl80211.h NL80211_IFTYPE_*}
 WIFI_IFTYPE_UNSPECIFIED = 0; {Unspecified type, driver decides}
 WIFI_IFTYPE_ADHOC       = 1; {Independent BSS member}
 WIFI_IFTYPE_STATION     = 2; {Managed BSS member}
 WIFI_IFTYPE_AP          = 3; {Access point}
 WIFI_IFTYPE_AP_VLAN     = 4; {VLAN interface for access points}
 WIFI_IFTYPE_WDS         = 5; {Wireless distribution interface}
 WIFI_IFTYPE_MONITOR     = 6; {Monitor interface receiving all frames}
 WIFI_IFTYPE_MESH_POINT  = 7; {Mesh point}
 WIFI_IFTYPE_P2P_CLIENT  = 8; {P2P client}
 WIFI_IFTYPE_P2P_GO      = 9; {P2P group owner}
 WIFI_IFTYPE_P2P_DEVICE  = 10; {P2P device interface type}
 WIFI_IFTYPE_OCB         = 11; {Outside Context of a BSS}
                         
 WIFI_NUM_IFTYPES        = 12; {Number of defined interface types}
 
 {WiFi Channel Types} {As per Linux nl80211.h NL80211_CHAN_*}
 WIFI_CHAN_NO_HT     = 0; {20 MHz, non-HT channel}
 WIFI_CHAN_HT20      = 1; {20 MHz HT channel}
 WIFI_CHAN_HT40MINUS = 2; {HT40 channel, secondary channel below the control channel}
 WIFI_CHAN_HT40PLUS  = 3; {HT40 channel, secondary channel above the control channel}
 
 {WiFi Channel Widths} {As per Linux nl80211.h NL80211_CHAN_WIDTH_*}
 WIFI_CHAN_WIDTH_20_NOHT = 0; {20 MHz, non-HT channel}
 WIFI_CHAN_WIDTH_20      = 1; {20 MHz HT channel}
 WIFI_CHAN_WIDTH_40      = 2; {40 MHz channel, the NL80211_ATTR_CENTER_FREQ1 attribute must be provided as well}
 WIFI_CHAN_WIDTH_80      = 3; {80 MHz channel, the NL80211_ATTR_CENTER_FREQ1 attribute must be provided as well}
 WIFI_CHAN_WIDTH_80P80   = 4; {80+80 MHz channel, the NL80211_ATTR_CENTER_FREQ1 and NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well}
 WIFI_CHAN_WIDTH_160     = 5; {160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 attribute must be provided as well}
 WIFI_CHAN_WIDTH_5       = 6; {5 MHz OFDM channel}
 WIFI_CHAN_WIDTH_10      = 7; {10 MHz OFDM channel}
 
 {WiFi Signal Types} {As per Linux cfg80211.h CFG80211_SIGNAL_TYPE_*}
 WIFI_SIGNAL_TYPE_NONE   = 0; {No signal strength information available}
 WIFI_SIGNAL_TYPE_MBM    = 1; {Signal strength in mBm (100*dBm)}
 WIFI_SIGNAL_TYPE_UNSPEC = 2; {Signal strength, increasing from 0 through 100}
 
 {WiFi TX power adjustment} {As per Linux nl80211.h NL80211_TX_POWER_*}
 WIFI_TX_POWER_AUTOMATIC = 0; {automatically determine transmit power}
 WIFI_TX_POWER_LIMITED   = 1; {limit TX power by the mBm parameter}
 WIFI_TX_POWER_FIXED     = 2; {fix TX power to the mBm parameter}
 
 {WiFi RX Flags} {As per Linux mac80211.h RX_FLAG_*}
 WIFI_RX_FLAG_MMIC_ERROR	        = (1 shl 0);  {Michael MIC error was reported on this frame}
 WIFI_RX_FLAG_DECRYPTED             = (1 shl 1);  {This frame was decrypted in hardware}
 WIFI_RX_FLAG_MMIC_STRIPPED         = (1 shl 3);  {The Michael MIC is stripped off this frame,verification has been done by the hardware}
 WIFI_RX_FLAG_IV_STRIPPED           = (1 shl 4);  {The IV/ICV are stripped from this frame}
 WIFI_RX_FLAG_FAILED_FCS_CRC        = (1 shl 5);  {Set this flag if the FCS check failed on	the frame}
 WIFI_RX_FLAG_FAILED_PLCP_CRC       = (1 shl 6);  {Set this flag if the PCLP check failed on the frame}
 WIFI_RX_FLAG_MACTIME_START         = (1 shl 7);  {The timestamp passed in the RX status MACTime field is valid and contains the time the first symbol of the MPDU was received}
 WIFI_RX_FLAG_SHORTPRE              = (1 shl 8);  {Short preamble was used for this frame}
 WIFI_RX_FLAG_HT                    = (1 shl 9);  {HT MCS was used and RateIndex is MCS index}
 WIFI_RX_FLAG_40MHZ                 = (1 shl 10); {HT40 (40 MHz) was used}
 WIFI_RX_FLAG_SHORT_GI              = (1 shl 11); {Short guard interval was used}
 WIFI_RX_FLAG_NO_SIGNAL_VAL         = (1 shl 12); {The signal strength value is not present. Valid only for data frames (mainly A-MPDU)}
 WIFI_RX_FLAG_HT_GF                 = (1 shl 13); {This frame was received in a HT-greenfield transmission}
 WIFI_RX_FLAG_AMPDU_DETAILS         = (1 shl 14); {A-MPDU details are known}
 WIFI_RX_FLAG_AMPDU_REPORT_ZEROLEN  = (1 shl 15); {Driver reports 0-length subframes}
 WIFI_RX_FLAG_AMPDU_IS_ZEROLEN      = (1 shl 16); {This is a zero-length subframe}
 WIFI_RX_FLAG_AMPDU_LAST_KNOWN      = (1 shl 17); {Last subframe is known}
 WIFI_RX_FLAG_AMPDU_IS_LAST         = (1 shl 18); {This subframe is the last subframe of the A-MPDU}
 WIFI_RX_FLAG_AMPDU_DELIM_CRC_ERROR = (1 shl 19); {A delimiter CRC error has been detected on this subframe}
 WIFI_RX_FLAG_AMPDU_DELIM_CRC_KNOWN = (1 shl 20); {The delimiter CRC field is known}
 WIFI_RX_FLAG_MACTIME_END           = (1 shl 21); {The timestamp passed in the RX status MACTime field is valid and contains the time the last symbol of the MPDU (including FCS) was received}
 WIFI_RX_FLAG_VHT                   = (1 shl 22); {HT MCS was used and RateIndex is MCS index}
 WIFI_RX_FLAG_LDPC                  = (1 shl 23); {LDPC was used}
 WIFI_RX_FLAG_STBC_MASK             = (1 shl 26) or (1 shl 27); {STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3}
 WIFI_RX_FLAG_10MHZ                 = (1 shl 28); {10 MHz (half channel) was used}
 WIFI_RX_FLAG_5MHZ                  = (1 shl 29); {5 MHz (quarter channel) was used}
 WIFI_RX_FLAG_AMSDU_MORE            = (1 shl 30); {Some drivers may prefer to report separate A-MSDU subframes instead of a one huge frame for performance reasons}
 WIFI_RX_FLAG_RADIOTAP_VENDOR_DATA  = (1 shl 31); {This frame contains vendor-specific radiotap data in the data (before the frame)}
 
 {WiFi RX VHT Flags} {As per Linux mac80211.h RX_VHT_FLAG_*}
 WIFI_RX_VHT_FLAG_80MHZ	 = (1 shl 0); {80 MHz was used}
 WIFI_RX_VHT_FLAG_160MHZ = (1 shl 1); {160 MHz was used}
 WIFI_RX_VHT_FLAG_BF     = (1 shl 2); {packet was beamformed}
 
{==============================================================================}
type
 {IEEE80211 specific types} {As per Linux ieee80211.h}
 {$PACKRECORDS 2}
 PIEEE80211Header = ^TIEEE80211Header; {From Linux ieee80211.h ieee80211_hdr}
 TIEEE80211Header = record {Not Packed}
  FrameControl:Word; {LE16}
  DurationId:Word;   {LE16}
  Address1:THardwareAddress;
  Address2:THardwareAddress;
  Address3:THardwareAddress;
  SequenceControl:Word; {LE16}
  Address4:THardwareAddress;
 end;
 {$PACKRECORDS DEFAULT}
 
 {$PACKRECORDS 2}
 PIEEE80211Header3Address = ^TIEEE80211Header3Address; {From Linux ieee80211.h ieee80211_hdr_3addr}
 TIEEE80211Header3Address = record {Not Packed}
  FrameControl:Word; {LE16}
  DurationId:Word;   {LE16}
  Address1:THardwareAddress;
  Address2:THardwareAddress;
  Address3:THardwareAddress;
  SequenceControl:Word; {LE16}
 end;
 {$PACKRECORDS DEFAULT}

 {$PACKRECORDS 2}
 PIEEE80211QOSHeader = ^TIEEE80211QOSHeader; {From Linux ieee80211.h ieee80211_qos_hdr}
 TIEEE80211QOSHeader = record {Not Packed}
  FrameControl:Word; {LE16}
  DurationId:Word;   {LE16}
  Address1:THardwareAddress;
  Address2:THardwareAddress;
  Address3:THardwareAddress;
  SequenceControl:Word; {LE16}
  QoSControl:Word;      {LE16}
 end; 
 {$PACKRECORDS DEFAULT}

 {$PACKRECORDS 2}
 PIEEE80211SHeader = ^TIEEE80211SHeader; {From Linux ieee80211.h ieee80211s_hdr}
 TIEEE80211SHeader = record {Not Packed}
  Flags:Byte;
  TTL:Byte;
  SequenceNumber:LongWord; {LE32}
  Address1:THardwareAddress;
  Address2:THardwareAddress;
 end;
 {$PACKRECORDS DEFAULT}

 PIEEE80211QuietIE = ^TIEEE80211QuietIE; {Quiet information element} {From Linux ieee80211.h ieee80211_quiet_ie}
 TIEEE80211QuietIE = packed record
  Count:Byte;
  Period:Byte;
  Duration:Word; {LE16}
  Offset:Word;   {LE16}
 end;
 
 PIEEE80211MeasurementIE = ^TIEEE80211MeasurementIE;  {Measurement Request/Report information element} {From Linux ieee80211.h ieee80211_msrment_ie}
 TIEEE80211MeasurementIE = packed record
  Token:Byte;
  Mode:Byte;
  TType:Byte; {Type}
  Request:array[0..0] of Byte; 
 end;
 
 PIEEE80211ChannelSwitchIE = ^TIEEE80211ChannelSwitchIE; {Channel Switch Announcement information element} {From Linux ieee80211.h ieee80211_channel_sw_ie}
 TIEEE80211ChannelSwitchIE = packed record
  Mode:Byte;
  NewChannel:Byte;
  Count:Byte; 
 end;
 
 PIEEE80211ExtChannelSwitchIE = ^TIEEE80211ExtChannelSwitchIE; {Extended Channel Switch Announcement element} {From Linux ieee80211.h ieee80211_ext_chansw_ie}
 TIEEE80211ExtChannelSwitchIE = packed record
  Mode:Byte;
  NewOperatingClass:Byte;
  NewChannel:Byte;
  Count:Byte; 
 end;
 
 PIEEE80211SecondaryChannelOffsetIE = ^TIEEE80211SecondaryChannelOffsetIE; {Secondary Channel Offset element} {From Linux ieee80211.h ieee80211_sec_chan_offs_ie}
 TIEEE80211SecondaryChannelOffsetIE = packed record
  SecondaryChannelOffset:Byte; {Secondary channel offset, uses IEEE80211_HT_PARAM_CHA_SEC_* values here}
 end;

 PIEEE80211MeshChannelSwitchParamsIE = ^TIEEE80211MeshChannelSwitchParamsIE; {Mesh Channel Switch Paramters element} {From Linux ieee80211.h ieee80211_mesh_chansw_params_ie}
 TIEEE80211MeshChannelSwitchParamsIE = packed record
  MeshTTL:Byte;
  MeshFlags:Byte;    {See IEEE80211_CHAN_SWITCH_PARAM_*}
  MeshReason:Word;   {LE16}
  MeshPreValue:Word; {LE16}
 end;
 
 PIEEE80211WidebandChannelSwitchIE = ^TIEEE80211WidebandChannelSwitchIE; {Wide bandwidth channel switch IE} {From Linux ieee80211.h ieee80211_wide_bw_chansw_ie}
 TIEEE80211WidebandChannelSwitchIE = packed record
  NewChannelWidth:Byte;
  NewCenterFreqSeg0:Byte;
  NewCenterFreqSeg1:Byte;
 end;
 
 PIEEE80211TrafficIndicationMapIE = ^TIEEE80211TrafficIndicationMapIE; {Traffic Indication Map information element} {From Linux ieee80211.h ieee80211_tim_ie}
 TIEEE80211TrafficIndicationMapIE = packed record
  DTIMCount:Byte;
  DTIMPeriod:Byte;
  BitmapControl:Byte;
  VirtualMap:array[0..0] of Byte; {Variable size: 1 - 251 bytes}
 end;

 PIEEE80211MeshConfigurationIE = ^TIEEE80211MeshConfigurationIE; {Mesh Configuration information element} {From Linux ieee80211.h ieee80211_meshconf_ie}
 TIEEE80211MeshConfigurationIE = packed record
  MeshConfPSEL:Byte;
  MeshConfPMetric:Byte;
  MeshconfCongest:Byte;
  MeshconfSynch:Byte;
  MeshConfAuth:Byte;
  MeshConfForm:Byte;
  MeshConfCap:Byte;
 end;
 
 PIEEE80211RootAnnouncementIE = ^TIEEE80211RootAnnouncementIE; {Root Announcement information element} {From Linux ieee80211.h ieee80211_rann_ie}
 TIEEE80211RootAnnouncementIE = packed record
  RannFlags:Byte;
  RannHopcount:Byte;
  RannTTL:Byte;
  RannAddress:THardwareAddress;
  RannSequence:LongWord; {LE32}
  RannInterval:LongWord; {LE32}
  RannMetric:LongWord;   {LE32}
 end;

 PIEEE80211TPCReportIE = ^TIEEE80211TPCReportIE; {TPC Report element} {From Linux ieee80211.h ieee80211_tpc_report_ie}
 TIEEE80211TPCReportIE = packed record
  TXPower:Byte;
  LinkMargin:Byte;
 end;
 
 TIEEE80211ManagementAuth = packed record {From Linux ieee80211.h ieee80211_mgmt}
  AuthAlgorithm:Word;   {LE16}
  AuthTransaction:Word; {LE16}
  StatusCode:Word;      {LE16}
  {Possibly followed by Challenge text}
  Variable:array[0..0] of Byte; 
 end; 
 
 TIEEE80211ManagementDeauth = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ReasonCode:Word; {LE16}
 end;

 TIEEE80211ManagementAssocRequest = packed record {From Linux ieee80211.h ieee80211_mgmt}
  CapabilitiesInfo:Word; {LE16}
  ListenInterval:Word;   {LE16}
  {followed by SSID and Supported rates}
  Variable:array[0..0] of Byte; 
 end;

 TIEEE80211ManagementAssocResponse = packed record {From Linux ieee80211.h ieee80211_mgmt}
  CapabilitiesInfo:Word; {LE16}
  StatusCode:Word;       {LE16}
  AID:Word;              {LE16}
  {followed by Supported rates}
  Variable:array[0..0] of Byte; 
 end;
 
 TIEEE80211ManagementReassocRequest = packed record {From Linux ieee80211.h ieee80211_mgmt}
  CapabilitiesInfo:Word; {LE16}
  ListenInterval:Word;   {LE16}
  CurrentAP:THardwareAddress;
  {followed by SSID and Supported rates}
  Variable:array[0..0] of Byte; 
 end;
 
 TIEEE80211ManagementDisassoc = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ReasonCode:Word; {LE16}
 end;
 
 TIEEE80211ManagementBeacon = packed record {From Linux ieee80211.h ieee80211_mgmt}
  Timestamp:Int64;       {LE64}
  BeaconInterval:Word;   {LE16}
  CapabilitiesInfo:Word; {LE16}
  {followed by some of SSID, Supported rates, FH Params, DS Params, CF Params, IBSS Params, TIM}
  Variable:array[0..0] of Byte; 
 end;
 
 TIEEE80211ManagementProbeRequest = packed record {From Linux ieee80211.h ieee80211_mgmt}
  {only variable items: SSID, Supported rates}
  Variable:array[0..0] of Byte; 
 end;
 
 TIEEE80211ManagementProbeResponse = packed record {From Linux ieee80211.h ieee80211_mgmt}
  Timestamp:Int64;       {LE64}
  BeaconInterval:Word;   {LE16}
  CapabilitiesInfo:Word; {LE16}
  {followed by some of SSID, Supported rates, FH Params, DS Params, CF Params, IBSS Params}
  Variable:array[0..0] of Byte; 
 end;

 TIEEE80211ManagementActionWMEAction = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  DialogToken:Byte;
  StatusCode:Byte;
  Variable:array[0..0] of Byte; 
 end;
 
 TIEEE80211ManagementActionChannelSwitch = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  Variable:array[0..0] of Byte; 
 end;
 
 TIEEE80211ManagementActionExtChannelSwitch = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  Data:TIEEE80211ExtChannelSwitchIE;
  Variable:array[0..0] of Byte; 
 end;
 
 TIEEE80211ManagementActionMeasurement = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  DialogToken:Byte;
  ElementID:Byte;
  Length:Byte;
  Element:TIEEE80211MeasurementIE;
 end;
 
 TIEEE80211ManagementActionAddBARequest = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  DialogToken:Byte;
  Capability:Word;          {LE16}
  Timeout:Word;             {LE16}
  StartSequenceNumber:Word; {LE16}
 end;
 
 TIEEE80211ManagementActionAddBAResponse = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  DialogToken:Byte;
  Status:Word;     {LE16}
  Capability:Word; {LE16}
  Timeout:Word;    {LE16}
 end;

 TIEEE80211ManagementActionDelBA = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  Params:Word;     {LE16}
  ReasonCode:Word; {LE16}
 end;
 
 TIEEE80211ManagementActionSelfProt = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  Variable:array[0..0] of Byte; 
 end;
 
 TIEEE80211ManagementActionMeshAction = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  Variable:array[0..0] of Byte; 
 end;

 TIEEE80211ManagementActionSAQuery = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  TransID:array[0..WLAN_SA_QUERY_TR_ID_LEN - 1] of Byte;
 end;

 TIEEE80211ManagementActionHTSMPS = packed record {From Linux ieee80211.h ieee80211_mgmt}
  Action:Byte;
  SMPSControl:Byte;
 end;

 TIEEE80211ManagementActionHTNotifyChannelWidth = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  ChannelWidth:Byte;
 end;
 
 TIEEE80211ManagementActionTDLSDiscoverResponse = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  DialogToken:Byte;
  Capability:Word; {LE16}
  Variable:array[0..0] of Byte; 
 end;
 
 TIEEE80211ManagementActionVHTOpmodeNotify = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  OperatingMode:Byte;
 end;

 TIEEE80211ManagementActionTPCReport = packed record {From Linux ieee80211.h ieee80211_mgmt}
  ActionCode:Byte;
  DialogToken:Byte;
  TPCElementID:Byte;
  TPCElementLength:Byte;
  TPC:TIEEE80211TPCReportIE;
 end;
 
 TIEEE80211ManagementAction = packed record {From Linux ieee80211.h ieee80211_mgmt}
  case Integer of
   0:(WMEAction:TIEEE80211ManagementAuth);
   1:(ChannelSwitch:TIEEE80211ManagementActionChannelSwitch);
   2:(ExtChannelSwitch:TIEEE80211ManagementActionExtChannelSwitch);
   3:(Measurement:TIEEE80211ManagementActionMeasurement);
   4:(AddBARequest:TIEEE80211ManagementActionAddBARequest);
   5:(AddBAResponse:TIEEE80211ManagementActionAddBAResponse);
   6:(DelBA:TIEEE80211ManagementActionDelBA);
   7:(SelfProt:TIEEE80211ManagementActionSelfProt);
   8:(MeshAction:TIEEE80211ManagementActionMeshAction);
   9:(SAQuery:TIEEE80211ManagementActionSAQuery);
   10:(HTSMPS:TIEEE80211ManagementActionHTSMPS);
   11:(HTNotifyChannelWidth:TIEEE80211ManagementActionHTNotifyChannelWidth);
   12:(TDLSDiscoverResponse:TIEEE80211ManagementActionTDLSDiscoverResponse);
   13:(VHTOpmodeNotify:TIEEE80211ManagementActionVHTOpmodeNotify);
   14:(TPCReport:TIEEE80211ManagementActionTPCReport);
 end;
 
 {$PACKRECORDS 2}
 PIEEE80211Management = ^TIEEE80211Management; {From Linux ieee80211.h ieee80211_mgmt}
 TIEEE80211Management = record {Not Packed}
  FrameControl:Word; {LE16}
  Duration:Word;     {LE16}
  DestinationAddress:THardwareAddress;
  SourceAddress:THardwareAddress;
  BSSID:THardwareAddress;
  SequenceControl:Word; {LE16}
  case Integer of
   0:(Auth:TIEEE80211ManagementAuth);
   1:(Deauth:TIEEE80211ManagementDeauth);
   2:(AssocRequest:TIEEE80211ManagementAssocRequest);
   3:(AssocResponse:TIEEE80211ManagementAssocResponse);
   4:(ReassocResponse:TIEEE80211ManagementAssocResponse);
   5:(ReassocRequest:TIEEE80211ManagementReassocRequest);
   6:(Disassoc:TIEEE80211ManagementDisassoc);
   7:(Beacon:TIEEE80211ManagementBeacon);
   8:(ProbeRequest:TIEEE80211ManagementProbeRequest);
   9:(ProbeResponse:TIEEE80211ManagementProbeResponse);
   10:(Action:TIEEE80211ManagementAction);
 end;
 {$PACKRECORDS DEFAULT}
  
 //To Do //IEEE80211_MIN_ACTION_SIZE from ieee80211.h
 
 PIEEE80211ManagementMICIE = ^TIEEE80211ManagementMICIE; {Management MIC information element (IEEE 802.11w)} {From Linux ieee80211.h ieee80211_mmie}
 TIEEE80211ManagementMICIE = packed record
  ElementID:Byte;
  Length:Byte;
  KeyID:Word; {LE16}
  SequenceNumber:array[0..5] of Byte;
  MIC:array[0..7] of Byte;
 end;
 
 PIEEE80211ManagementMIC16IE = ^TIEEE80211ManagementMIC16IE; {Management MIC information element (IEEE 802.11w) for GMAC and CMAC-256} {From Linux ieee80211.h ieee80211_mmie_16}
 TIEEE80211ManagementMIC16IE = packed record
  ElementID:Byte;
  Length:Byte;
  KeyID:Word; {LE16}
  SequenceNumber:array[0..5] of Byte;
  MIC:array[0..15] of Byte;
 end;

 PIEEE80211VendorIE = ^TIEEE80211VendorIE; {Vendor information element} {From Linux ieee80211.h  ieee80211_vendor_ie}
 TIEEE80211VendorIE = packed record
  ElementID:Byte;
  Length:Byte;
  OUI:array[0..2] of Byte;
  OUIType:Byte;
 end;

 PIEEE80211WMMACParam = ^TIEEE80211WMMACParam; {From Linux ieee80211.h ieee80211_wmm_ac_param}
 TIEEE80211WMMACParam = packed record
  ACI_AIFSN:Byte; {AIFSN, ACM, ACI}
  CW:Byte;        {ECWmin, ECWmax (CW = 2^ECW - 1)}
  TXOpLimit:Word; {LE16}
 end;

 PIEEE80211WMMParamIE = ^TIEEE80211WMMParamIE; {WMM Param information element} {From Linux ieee80211.h ieee80211_wmm_param_ie}
 TIEEE80211WMMParamIE = packed record
  ElementID:Byte;          {Element ID: 221 (= $dd);}
  Length:Byte;             {Length: 24}
  {required fields for WMM version 1}
  OUI:array[0..2] of Byte; {00:50:f2}
  OUIType:Byte;            {2}
  OUISubType:Byte;         {1}
  Version:Byte;            {1 for WMM version 1.0}
  QoSInfo:Byte;            {AP/STA specific QoS info}
  Reserved:Byte;           {0}
  {AC_BE, AC_BK, AC_VI, AC_VO}
  AC:array[0..3] of TIEEE80211WMMACParam;
 end;

 {Control frames}
 {$PACKRECORDS 2}
 PIEEE80211RTS = ^TIEEE80211RTS;
 TIEEE80211RTS = record {Not Packed}
  FrameControl:Word; {LE16}
  Duration:Word;     {LE16}
  ReceiverAddress:THardwareAddress;
  TransmitterAddress:THardwareAddress;
 end;
 {$PACKRECORDS DEFAULT}
 
 {$PACKRECORDS 2}
 PIEEE80211CTS = ^TIEEE80211CTS;
 TIEEE80211CTS = record {Not Packed}
  FrameControl:Word; {LE16}
  Duration:Word;     {LE16}
  ReceiverAddress:THardwareAddress;
 end;
 {$PACKRECORDS DEFAULT}

 {$PACKRECORDS 2}
 PIEEE80211PSPoll = ^TIEEE80211PSPoll;
 TIEEE80211PSPoll = record {Not Packed}
  FrameControl:Word; {LE16}
  AID:Word;          {LE16}
  BSSID:THardwareAddress;
  TransmitterAddress:THardwareAddress;
 end;
 {$PACKRECORDS DEFAULT}

 {TDLS}
 PIEEE80211ChannelSwitchTiming = ^TIEEE80211ChannelSwitchTiming; {Channel switch timing} {From Linux ieee80211.h ieee80211_ch_switch_timing}
 TIEEE80211ChannelSwitchTiming = packed record
  SwitchTime:Word;    {LE16}
  SwitchTimeout:Word; {LE16}
 end;
 
 PIEEE80211TDLSLinkIdentifierIE = ^TIEEE80211TDLSLinkIdentifierIE; {Link-id information element} {From Linux ieee80211.h  ieee80211_tdls_lnkie}
 TIEEE80211TDLSLinkIdentifierIE = packed record
  ElementID:Byte; {Link Identifier IE}
  Length:Byte;
  BSSID:THardwareAddress;
  InitiatingStation:THardwareAddress;
  RespondingStation:THardwareAddress;
 end;

 TIEEE80211TDLSDataSetupRequest = packed record
  DialogToken:Byte;
  Capability:Word; {LE16}
  Variable:array[0..0] of Byte;
 end;

 TIEEE80211TDLSDataSetupResponse = packed record
  StatusCode:Word; {LE16}
  DialogToken:Byte;
  Capability:Word; {LE16}
  Variable:array[0..0] of Byte;
 end;

 TIEEE80211TDLSDataSetupConfirm = packed record
  StatusCode:Word; {LE16}
  DialogToken:Byte;
  Variable:array[0..0] of Byte;
 end;

 TIEEE80211TDLSDataTeardown = packed record
  ReasonCode:Word; {LE16}
  Variable:array[0..0] of Byte;
 end;

 TIEEE80211TDLSDataDiscoverRequest = packed record
  DialogToken:Byte;
  Variable:array[0..0] of Byte;
 end;

 TIEEE80211TDLSDataChannelSwitchRequest = packed record
  TargetChannel:Byte;
  OperatingClass:Byte;
  Variable:array[0..0] of Byte;
 end;

 TIEEE80211TDLSDataChannelSwitchResponse = packed record
  StatusCode:Word; {LE16}
  Variable:array[0..0] of Byte;
 end;
 
 PIEEE80211TDLSData = ^TIEEE80211TDLSData; {TDLS Data} {From Linux ieee80211.h ieee80211_tdls_data}
 TIEEE80211TDLSData = packed record
  DestinationAddress:THardwareAddress;
  SourceAddress:THardwareAddress;
  EtherType:Word; {BE16 (As per Ethernet Header)}
  PayloadType:Byte;
  Category:Byte;
  ActionCode:Byte;
  case Integer of
   0:(SetupRequest:TIEEE80211TDLSDataSetupRequest);
   1:(SetupResponse:TIEEE80211TDLSDataSetupResponse);
   2:(SetupConfirm:TIEEE80211TDLSDataSetupConfirm);
   3:(Teardown:TIEEE80211TDLSDataTeardown);
   4:(DiscoverRequest:TIEEE80211TDLSDataDiscoverRequest);
   5:(ChannelSwitchRequest:TIEEE80211TDLSDataChannelSwitchRequest);
   6:(ChannelSwitchResponse:TIEEE80211TDLSDataChannelSwitchResponse);
 end;

 PIEEE80211P2PNOADescription = ^TIEEE80211P2PNOADescription; {Notice of Absence attribute - described in P2P spec 4.1.14} {From Linux ieee80211.h ieee80211_p2p_noa_desc}
 TIEEE80211P2PNOADescription = packed record
  Count:Byte;
  Duration:LongWord; {LE32}
  Interval:LongWord; {LE32}
  StartTime:LongWord; {LE32}
 end;
 
 PIEEE80211P2PNOAAttribute = ^TIEEE80211P2PNOAAttribute; {Notice of Absence attribute - described in P2P spec 4.1.14} {From Linux ieee80211.h ieee80211_p2p_noa_attr}
 TIEEE80211P2PNOAAttribute = packed record
  Index:Byte;
  OPPPS_CTWindow:Byte;
  Description:array[0..IEEE80211_P2P_NOA_DESC_MAX - 1] of TIEEE80211P2PNOADescription;
 end;
 
 PIEEE80211Bar = ^TIEEE80211Bar; {HT Block Ack Request} {From Linux ieee80211.h ieee80211_bar}
 TIEEE80211Bar = packed record {This structure refers to "HT BlockAckReq" as described in 802.11n draft section 7.2.1.7.1}
  FrameControl:Word;       {LE16}
  Duration:Word;           {LE16}
  ReceiverAddress:THardwareAddress;
  TransmitterAddress:THardwareAddress;
  Control:Word;            {LE16}
  StartSequenceNumber:Word;{LE16}
 end;
 
 PIEEE80211MCSInfo = ^TIEEE80211MCSInfo; {HT MCS information} {From Linux ieee80211.h ieee80211_mcs_info}
 TIEEE80211MCSInfo = packed record
  RXMask:array[0..IEEE80211_HT_MCS_MASK_LEN - 1] of Byte; {RX mask}
  RXHighest:Word;      {Highest supported RX rate (LE16). If set represents the highest supported RX data rate in units of 1 Mbps. If this field is 0 this value should not be used to consider the highest RX data rate supported.}
  TXParams:Byte;       {TX parameters}
  Reserved:array[0..2] of Byte;
 end;
 
 PIEEE80211HTCapabilities = ^TIEEE80211HTCapabilities; {HT capabilities} {From Linux ieee80211.h ieee80211_ht_cap}
 TIEEE80211HTCapabilities = packed record {This structure is the "HT capabilities element" as described in 802.11n D5.0 7.3.2.57}
  CapabilityInfo:Word;  {LE16}
  AMPDUParamsInfo:Byte;
  MCS:TIEEE80211MCSInfo;  {16 bytes MCS information}
  ExtendedHTCapInfo:Word; {LE16}
  TXBFCapInfo:LongWord;   {LE32}
  AntennaSelectionInfo:Byte;
 end;
 
 PIEEE80211HTOperation = ^TIEEE80211HTOperation; {HT operation IE} {From Linux ieee80211.h ieee80211_ht_operation}
 TIEEE80211HTOperation = packed record
  PrimaryChannel:Byte;
  HTParam:Byte;
  OperationMode:Word; {LE16}
  STBCParam:Word;     {LE16}
  BasicSet:array[0..15] of Byte;
 end; 
 
 PIEEE80211VHTMCSInfo = ^TIEEE80211VHTMCSInfo; {VHT MCS information} {From Linux ieee80211.h ieee80211_vht_mcs_info}
 TIEEE80211VHTMCSInfo = packed record
  RXMCSMap:Word;  {LE16} {RX MCS map 2 bits for each stream, total 8 streams}
  RXHighest:Word; {LE16} {Indicates highest long GI VHT PPDU data rate STA can receive. Rate expressed in units of 1 Mbps. If this field is 0 this value should not be used to consider the highest RX data rate supported. The top 3 bits of this field are reserved.}
  TXMCSMap:Word;  {LE16} {TX MCS map 2 bits for each stream, total 8 streams}
  TXHighest:Word; {LE16} {Indicates highest long GI VHT PPDU data rate STA can transmit. Rate expressed in units of 1 Mbps. If this field is 0 this value should not be used to consider the highest TX data rate supported. The top 3 bits of this field are reserved.}
 end;
 
 PIEEE80211VHTCapabilities = ^TIEEE80211VHTCapabilities; {VHT capabilities} {From Linux ieee80211.h ieee80211_vht_cap}
 TIEEE80211VHTCapabilities = packed record {This structure is the "VHT capabilities element" as described in 802.11ac D3.0 8.4.2.160}
  VHTCapabilityInfo:LongWord; {LE32} {VHT capability info}
  SupportedMCS:TIEEE80211VHTMCSInfo; {VHT MCS supported rates}
 end;
 
 PIEEE80211VHTOperation = ^TIEEE80211VHTOperation; {VHT operation IE} {From Linux ieee80211.h ieee80211_vht_operation}
 TIEEE80211VHTOperation = packed record {This structure is the "VHT operation element" as described in 802.11ac D3.0 8.4.2.161}
  ChannelWidth:Byte;
  CenterFreqSeg1Idx:Byte;
  CenterFreqSeg2Idx:Byte;
  BasicMCSSet:Word; {LE16}
 end; 
 
 TIEEE80211CountryTripletIEChannels = packed record 
  FirstChannel:Byte;
  NumChannels:Byte;
  MaxPower:ShortInt;
 end;
 
 TIEEE80211CountryTripletIEExt = packed record 
  RegExtensionID:Byte;
  RegClass:Byte;
  CoverageClass:Byte;
 end;
 
 {Channels numbers in the IE must be monotonically increasing if dot11RegulatoryClassesRequired is not true.
  If dot11RegulatoryClassesRequired is true consecutive subband triplets following a regulatory triplet shall have monotonically increasing first_channel number fields.
  Channel numbers shall not overlap.  Note that MaxPower is signed}
 PIEEE80211CountryTripletIE = ^TIEEE80211CountryTripletIE; {Country Triplet IE} {From Linux ieee80211.h ieee80211_country_ie_triplet}
 TIEEE80211CountryTripletIE = packed record 
  case Integer of
   0:(Channels:TIEEE80211CountryTripletIEChannels);
   1:(Ext:TIEEE80211CountryTripletIEExt);
 end;

 PIEEE80211TimeoutIntervalIE = ^TIEEE80211TimeoutIntervalIE; {Timeout Interval element} {From Linux ieee80211.h ieee80211_timeout_interval_ie}
 TIEEE80211TimeoutIntervalIE = packed record 
  IntervalType:Byte;
  Value:LongWord; {LE32}
 end; 
 
 PIEEE80211TSpecIE = ^TIEEE80211TSpecIE; {WMM/802.11e Tspec Element} {From Linux ieee80211.h ieee80211_tspec_ie}
 TIEEE80211TSpecIE = packed record 
  ElementID:Byte;
  Length:Byte;
  OUI:array[0..2] of Byte;
  OUIType:Byte;
  OUISubType:Byte;
  Version:Byte;
  TSInfo:Word; {LE16}
  TSInfoReserved:Byte;
  NominalMSDU:Word; {LE16}
  MaxMSDU:Word; {LE16}
  MinServiceInt:LongWord; {LE32}
  MaxServiceInt:LongWord; {LE32}
  InactivityInt:LongWord; {LE32}
  SuspensionInt:LongWord; {LE32}
  ServiceStartTime:LongWord; {LE32}
  MinDataRate:LongWord; {LE32}
  MeanDataRate:LongWord; {LE32}
  PeakDataRate:LongWord; {LE32}
  MaxBurstSize:LongWord; {LE32}
  DelayBound:LongWord; {LE32}
  MinPhyRate:LongWord; {LE32}
  SBA:Word; {LE16}
  MediumTime:Word; {LE16}
 end;
 
 {These structures are NOT defined by IEEE80211 but are internal to the implementation}
 PIEEE80211Channel = ^TIEEE80211Channel; {From Linux cfg80211.h ieee80211_channel}
 TIEEE80211Channel = record
  Band:LongWord;             {IEEE80211_BAND_2GHZ etc}
  CenterFrequency:Word;
  HardwareChannel:Word;
  Flags:LongWord;            {IEEE80211_CHAN_DISABLED etc}
  MaxAntennaGain:LongInt;
  MaxPower:LongInt;
  MaxRegPower:LongInt;
  BeaconFound:LongBool;
  //To Do //ieee80211_channel
 end;
  
 PIEEE80211Channels = ^TIEEE80211Channels;
 TIEEE80211Channels = array[0..0] of TIEEE80211Channel;
 
 PIEEE80211Rate = ^TIEEE80211Rate; {From Linux cfg80211.h ieee80211_rate}
 TIEEE80211Rate = record
  Flags:LongWord;            {rate-specific flags}
  BitRate:Word;              {bitrate in units of 100 Kbps}
  HardwareRate:Word;         {driver/hardware value for this rate}
  HardwareRateShort:Word;    {driver/hardware value for this rate when short preamble is used}
 end;
  
 PIEEE80211Rates = ^TIEEE80211Rates;
 TIEEE80211Rates = array[0..0] of TIEEE80211Rate;
 
 PIEEE80211ChannelDefinition = ^TIEEE80211ChannelDefinition;
 TIEEE80211ChannelDefinition = record
  Channel:PIEEE80211Channel; {Control channel}
  Width:LongWord;            {Channel width (See WIFI_CHAN_WIDTH_*)}
  CenterFrequency1:LongWord; {Center frequency of first segment}
  CenterFrequency2:LongWord; {Center frequency of second segment (only with 80+80 MHz)}
 end;
  
 PIEEE80211StationHTCap = ^TIEEE80211StationHTCap; {802.11n HT capabilities for an STA} {From Linux cfg80211.h ieee80211_sta_ht_cap}
 TIEEE80211StationHTCap = record
  Capabilities:LongWord;     {HT capabilities map as described in 802.11n spec (eg IEEE80211_HT_CAP_*)}
  HTSupported:LongBool;      {True if HT supported by the STA}
  AMPDUFactor:LongWord;      {Maximum A-MPDU length factor}
  AMPDUDensity:LongWord;     {Minimum A-MPDU spacing}
  MCS:TIEEE80211MCSInfo;     {Supported MCS rates}
 end;

 PIEEE80211StationVHTCap = ^TIEEE80211StationVHTCap; {802.11ac VHT capabilities for an STA} {From Linux cfg80211.h ieee80211_sta_vht_cap}
 TIEEE80211StationVHTCap = record
  Capabilities:LongWord;       {VHT capabilities map as described in 802.11ac spec (eg IEEE80211_VHT_CAP_*)}
  VHTSupported:LongBool;       {True if VHT supported by the STA}
  VHTMCS:TIEEE80211VHTMCSInfo; {Supported VHT MCS rates}
 end;
 
 PIEEE80211SupportedBand = ^TIEEE80211SupportedBand;  {From Linux cfg80211.h ieee80211_supported_band}
 TIEEE80211SupportedBand = record
  Channels:PIEEE80211Channels;
  Rates:PIEEE80211Rates;
  ChannelCount:LongWord;
  RateCount:LongWord;
  HTCapabilities:TIEEE80211StationHTCap;
  VHTCapabilities:TIEEE80211StationVHTCap;
 end;
 
 PIEEE80211BSSConfiguration = ^TIEEE80211BSSConfiguration;
 TIEEE80211BSSConfiguration = record  {From Linux mac80211.h ieee80211_bss_conf}
  BSSID:PByte;                 {The BSSID for this BSS}
  {Association related}        
  Associated:LongBool;         {Association status}
  IBSSJoined:LongBool;         {Indicates whether this station is part of an IBSS or not}
  IBSSCreator:LongBool;        {Indicates if a new IBSS network is being created}
  AssociationID:Word;          {Association ID number, valid only when Associated is true}
  {ERP related}                
  UseCTSProtection:LongBool;   {Use CTS protection}
  UseShortPreamble:LongBool;   {Use 802.11b short preamble (if the hardware cannot handle this it must set the IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE flag)}
  UseShortSlot:LongBool;       {Use short slot time (only relevant for ERP) (if the hardware cannot handle this it must set the IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE flag)}
  EnableBeacon:LongBool;       {Whether beaconing should be enabled or not}
  DTIMPeriod:Byte;             {No of beacons before the next DTIM, for beaconing, valid in station mode only if after the driver was notified with the BSS_CHANGED_BEACON_INFO flag, will be non-zero then}
  BeaconInterval:Word;         {Beacon interval}
  AssociationCapability:Word;  {Capabilities taken from association resp}
  SyncTSF:Int64;               {Last beacon's/probe response's TSF timestamp (could be old as it may have been received during scanning long ago)}
  SyncDeviceTS:LongWord;       {The device timestamp corresponding to the SyncTSF}
  SyncDTIMCount:Byte;          {Only valid when IEEE80211_HW_TIMING_BEACON_ONLY is requested}
  BasicRates:LongWord;         {Bitmap of basic rates, each bit stands for an index into the rate table configured by the driver in the current band}
  BeaconRate:PIEEE80211Rate;   {Associated AP's beacon TX rate}
  MulticastRate:array[0..IEEE80211_NUM_BANDS - 1] of Integer; {Per-band multicast rate index + 1 (0: disabled)}
  HTOperationMode:Word;        {HT operation mode like in  TIEEE80211HTOperation}
  CQMRSSIThreshold:LongInt;    {Connection quality monitor RSSI threshold, a zero value implies disabled}
  CQMRSSIHysteresis:LongWord;  {Connection quality monitor RSSI hysteresis}
  ChannelDefinition:TIEEE80211ChannelDefinition; {Channel definition for this BSS (the hardware might be configured a higher bandwidth than this BSS uses)}
  //__be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; //To Do
  //int arp_addr_cnt;                                      //To Do
  QoS:LongBool;                {This is a QoS-enabled BSS}
  Idle:LongBool;               {This interface is idle. There's also a global idle flag in the hardware config which may be more appropriate depending on what your driver/device needs to do}
  Powersave:LongBool;          {Power-save mode (STA only). This flag is NOT affected by offchannel/dynamic_ps operations}
  SSID:array[0..IEEE80211_MAX_SSID_LEN - 1] of Byte; {The SSID of the current interface. Valid in AP and IBSS mode}
  SSIDLength:LongWord;         {Length of SSID given in SSID}
  HiddenSSID:LongBool;         {The SSID of the current interface is hidden. Only valid in AP-mode}
  TXPower:LongInt;             {TX power in dBm}
  TXPowerType:LongWord;        {TX power adjustment used to control per packet Transmit Power Control (TPC) in driver for the current interface (WIFI_TX_POWER_*)}
  P2PNOAAttribute:TIEEE80211P2PNOAAttribute; {P2P NoA attribute for P2P powersave}
 end;
 
 PIEEE80211RXStatus = ^TIEEE80211RXStatus;
 TIEEE80211RXStatus = record  {From Linux mac80211.h ieee80211_rx_status}
  MACTime:Int64;            {Value in microseconds of the 64-bit Time Synchronization Function (TSF) timer when the first data symbol (MPDU) arrived at the hardware}
  DeviceTimestamp:LongWord; {Arbitrary timestamp for the device} //To Do //Remove ?
  AMPDUReference:LongWord;  {A-MPDU reference number, must be a different value for each A-MPDU but the same for each subframe within one A-MPDU}
  Flags:LongWord;           {WIFI_RX_FLAG_*}
  Frequency:Word;           {Frequency the radio was tuned to when receiving this frame, in MHz}
  VHTFlags:Byte;            {WIFI_RX_VHT_FLAG_*}
  RateIndex:Byte;           {Index of data rate into band's supported rates or MCS index if HT or VHT is used (WIFI_RX_FLAG_HT/WIFI_RX_FLAG_VHT)}
  VHTNSS:Byte;              {Number of streams (VHT only)}
  RXFlags:Byte;             {Internal RX flags (IEEE80211_RX_*)}
  Band:Byte;                {The active band when this frame was received}
  Antenna:Byte;             {Antenna used}
  Signal:Byte;              {Signal strength when receiving this frame, either in dBm, in dB or	unspecified depending on the hardware capabilities flags IEEE80211_HW_SIGNAL_*}
  Chains:Byte;              {Bitmask of receive chains for which separate signal strength values were filled}
  ChainSignal:array[0..IEEE80211_MAX_CHAINS - 1] of Byte; {Per-chain signal strength, in dBm (unlike Signal, doesn't support dB or unspecified units)}
  AMPDUDelimiterCRC:Byte;   {A-MPDU delimiter CRC}
 end;
 
 PIEEE80211TXInfo = ^TIEEE80211TXInfo;
 TIEEE80211TXInfo = record  {From Linux mac80211.h ieee80211_tx_info}
  //To Do //From ieee80211_tx_info
 end;
 
 PIEEE80211InformationElements = ^TIEEE80211InformationElements;  {From Linux ieee80211_i.h ieee802_11_elems}
 TIEEE80211InformationElements = record
  Address:Pointer;
  Size:LongWord;
  
  {Information Elements}
  LinkIdentifier:PIEEE80211TDLSLinkIdentifierIE;
  ChannelSwitchTiming:PIEEE80211ChannelSwitchTiming;
  ExtendedCapabilities:Pointer;
  SSID:Pointer; 
  SupportedRates:Pointer;
  DSParameters:Pointer;
  TrafficIndicationMap:PIEEE80211TrafficIndicationMapIE;
  ChallengeText:Pointer;
  RSN:Pointer;
  ERPInfo:Pointer;
  ExtendedSupportedRates:Pointer;
  WMMInfo:Pointer;
  WMMParam:Pointer;
  HTCapabilities:PIEEE80211HTCapabilities; 
  HTOperation:PIEEE80211HTOperation;
  VHTCapabilities:PIEEE80211VHTCapabilities;
  VHTOperation:PIEEE80211VHTOperation;
  MeshConfiguration:PIEEE80211MeshConfigurationIE;
  MeshID:Pointer;
  PeerManagement:Pointer;
  MeshAwakeWindow:Word; {LE16}
  PREQ:Pointer;
  PREP:Pointer;
  PERR:Pointer;
  RootAnnouncement:PIEEE80211RootAnnouncementIE;
  ChannelSwitch:PIEEE80211ChannelSwitchIE;
  ExtChannelSwitch:PIEEE80211ExtChannelSwitchIE;
  WidebandChannelSwitch:PIEEE80211WidebandChannelSwitchIE;
  Country:Pointer;
  PowerConstraint:Pointer;
  CiscoDTPC:Pointer;
  TimeoutInterval:PIEEE80211TimeoutIntervalIE;
  OpmodeNotification:Pointer;
  SecondaryChannelOffset:PIEEE80211SecondaryChannelOffsetIE;
  MeshChannelSwitchParams:PIEEE80211MeshChannelSwitchParamsIE;
  
  {Lengths of Elements}
  ExtendedCapabilitiesLength:Byte;
  SSIDLength:Byte;
  SupportedRatesLength:Byte;
  TrafficIndicationMapLength:Byte; 
  ChallengeTextLength:Byte; 
  RSNLength:Byte;
  ExtendedSupportedRatesLength:Byte;
  WMMInfoLength:Byte; 
  WMMParamLength:Byte; 
  MeshIDLength:Byte; 
  PeerManagementLength:Byte; 
  PREQLength:Byte; 
  PREPLength:Byte; 
  PERRLength:Byte; 
  CountryLength:Byte;
 end;
 
{==============================================================================}
type
 {WiFi specific types}
 PWiFiDeviceEvent = ^TWiFiDeviceEvent;
 TWiFiDeviceEvent = record
  Timer:TTimerHandle;
  Device:PNetworkDevice;
 end;
 
 {WiFi Hardware}
 PWiFiHardware = ^TWiFiHardware;
 TWiFiHardware = record
  Flags:LongWord;                     {Hardware flags (See IEEE80211_HW_*)}
  ExtraTXHeadroom:LongWord;           {Headroom to reserve in each transmit buffer for use by the driver (e.g. for transmit headers)}
  ExtraBeaconTailroom:LongWord;       {Tailroom to reserve in each beacon tx buffer. Can be used by drivers to add extra IE}
  MaxListenInterval:LongWord;         {Max listen interval in units of beacon interval that HW supports}
  MaxSignal:LongInt;                  {Maximum value for signal (RSSI) in RX information}
  MaxRates:LongWord;                  {Maximum number of alternate rate retry stages the hw can handle}
  MaxReportRates:LongWord;            {Maximum number of alternate rate retry stages the hw can report back}
  MaxRateTries:LongWord;              {Maximum number of tries for each stage}
  MaxRXAggregationSubframes:LongWord; {Maximum buffer size (number of sub-frames) to be used for A-MPDU block ack receiver aggregation}
  MaxTXAggregationSubframes:LongWord; {Maximum number of subframes in an aggregate an HT driver will transmit, used by the peer as a hint to size its reorder buffer}
  //To Do //From ieee80211_hw
 end;
 
 {WiFi Configuration}
 PWiFiConfiguration = ^TWiFiConfiguration;
 TWiFiConfiguration = record
  Flags:LongWord;                     {Configuration flags (See IEEE80211_CONF_*)}
  PowerLevel:LongInt;                 {Requested transmit power (in dBm), backward compatibility value only that is set to the minimum of all interfaces}
  ListenInterval:LongWord;            {Listen interval in units of beacon interval}
  LongFrameMaxTXCount:LongWord;       {Maximum number of transmissions for a "long" frame (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11, but actually means the number of transmissions not the number of retries}
  ShortFrameMaxTXCount:LongWord;      {Maximum number of transmissions for a "short" frame, called "dot11ShortRetryLimit" in 802.11, but actually means the number of transmissions not the number of retries}
  RadarEnabled:LongBool;              {Whether radar detection is enabled}
  SMPSMode:LongWord;                  {Spatial multiplexing powersave mode. Note that IEEE80211_SMPS_STATIC is used when the device is not configured for an HT channel}
  ChannelDefinition:TIEEE80211ChannelDefinition; {The channel definition to tune to}
  //To Do //From ieee80211_conf
 end;
 
 {WiFi Interface}
 PWiFiInterface = ^TWiFiInterface;
 TWiFiInterface = record
  InterfaceType:LongWord;                      {Type of this virtual interface (WIFI_IFTYPE_*)}
  Address:THardwareAddress;                    {Address of this interface}
  BSSConfiguration:TIEEE80211BSSConfiguration; {BSS configuration for this interface, either our own or the BSS we're associated to}
  
  DriverFlags:LongWord;                        {Interface specific flags (IEEE80211_VIF_*)}
  //To Do //From ieee80211_vif and ieee80211_sub_if_data
 end;
 
 {WiFi Device}
 PWiFiDevice = ^TWiFiDevice;
 
 {WiFi Device Methods}
 TWiFiDeviceConfigure = function(WiFi:PWiFiDevice;Flags:LongWord):LongWord;
 TWiFiDeviceConfigureFilter = function(WiFi:PWiFiDevice;var Filter:LongWord):LongWord;
 TWiFiDeviceConfigureInterface = function(WiFi:PWiFiDevice;Interrface:PWiFiInterface):LongWord;
 //To Do
 
 TWiFiDevice = record
  {Network Properties}
  Network:TNetworkDevice;
  {WiFi Properties}
  WiFiFlags:LongWord;                 {Flags for this WiFi device (eg WIFI_FLAG_*)}
  WiFiState:LongWord;                 {State of this WiFi device (eg WIFI_STATE_*)}
  WiFiStatus:LongWord;                {Status of this WiFi device (eg WIFI_STATUS_*)}         
  WiFiFeatures:LongWord;              {Features of this WiFi device (eg WIFI_FEATURE_*)}         
  DeviceConfigure:TWiFiDeviceConfigure;
  DeviceConfigureFilter:TWiFiDeviceConfigureFilter;
  DeviceConfigureInterface:TWiFiDeviceConfigureInterface;
  
  //To Do //From wiphy
  PermanentAddress:THardwareAddress;  {The permanent MAC address of this device}
  AddressMask:THardwareAddress;       {If the device supports multiple MAC addresses by masking, set this to a mask with variable bits set to 1, e.g. if the last four bits are variable then set it to 00-00-00-00-00-0f}
  Addresses:PHardwareAddresses;       {If the device has more than one address, set this to a list of addresses (6 bytes each). The first one will be used by default for PermanentAddress. In this case, the mask should be set to all zeroes}
  AddressCount:LongWord;              {The number of addresses in Addresses array}
  InterfaceModes:LongWord;            {Bitmask of interfaces types valid for this wifi, must be set by driver (eg 1 shl WIFI_IFTYPE_*)}
  
  SignalType:LongWord;                {Signal type reported in bss}
  RetryShort:LongWord;                {Retry limit for short frames (dot11ShortRetryLimit)}
  RetryLong:LongWord;                 {Retry limit for long frames (dot11LongRetryLimit)}
  FragThreshold:LongWord;             {Fragmentation threshold (dot11FragmentationThreshold); -1 = fragmentation disabled, only odd values >= 256 used}
  RTSThreshold:LongWord;              {RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled}
  CoverageClass:LongWord;             {Current coverage class}
  MaxCSACounters:Byte;                {Number of supported csa_counters in beacons and probe responses}
  
  MaxScanSSIDs:Byte;                  {Maximum number of SSIDs the device can scan for in any given scan}
  MaxScanIELength:Word;               {Maximum length of user-controlled IEs device can add to probe request frames transmitted during a scan, must not include fixed IEs like supported rates}

  MaxRemainOnChannelDuration:Word;    {Maximum time a remain-on-channel operation may request, if implemented}
  
  CipherSuites:PLongWord;             //To Do
  CipherSuiteCount:LongWord;
  
  Bands:array[0..IEEE80211_NUM_BANDS - 1] of PIEEE80211SupportedBand;
  
  //To Do //From ieee80211_local
   //To Do //Scan and BSS info
   
  RXChains:LongWord;                  {Number of RX chains the hardware has}
  TXHeadroom:LongWord;                {Required headroom for hardware}
  ScanIELength:LongWord;
  
  Hardware:TWiFiHardware;             {Hardware information and state}
  Configuration:TWiFiConfiguration;   {Configuration of the device}
  Interrface:TWiFiInterface;          {Interface of the device}
  
  {Driver Properties}
  //To Do
 end; 
 
{==============================================================================}
type
 {WiFi specific classes}
 //TWiFiServiceSet = class;
 TWiFiAdapter = class(TNetworkAdapter)
   constructor Create(AManager:TAdapterManager;ADevice:PNetworkDevice;const AName:String);
  private
   {Internal Variables}

   {Status Variables}
   FDefaultAddress:THardwareAddress;
   FHardwareAddress:THardwareAddress;
   FBroadcastAddress:THardwareAddress;
   FMulticastAddresses:TMulticastAddresses;
   
   {Internal Methods}
   function CheckReceiveFrame(APacket:PNetworkPacket;AStatus:PIEEE80211RXStatus):Boolean;
   
   function ReceiveHandler(APacket:PNetworkPacket;AStatus:PIEEE80211RXStatus):Boolean;
   
   function ScanReceiver(APacket:PNetworkPacket;AStatus:PIEEE80211RXStatus):Boolean;
   function DataReceiver(APacket:PNetworkPacket;AStatus:PIEEE80211RXStatus):Boolean;
  protected
   {Inherited Methods}
   procedure SetStatus(AStatus:Integer); override;
  public
   {Public Methods}
   function AddTransport(APacketType,AFrameType:Word;const APacketName:String;APacketHandler:TAdapterPacketHandler):THandle; override;
   function RemoveTransport(AHandle:THandle;APacketType:Word):Boolean; override;

   function GetMTU(AHandle:THandle):Word; override;

   function SendPacket(AHandle:THandle;ADest:Pointer;APacket:PPacketFragment;ASize:Integer):Boolean; override;

   function ClearStatistics(AHandle:THandle):Boolean; override;
   function GetStatistics(AHandle:THandle):TAdapterStatistics; override;
   
   function GetDefaultAddress(AHandle:THandle):THardwareAddress; override;
   function GetHardwareAddress(AHandle:THandle):THardwareAddress; override;
   function SetHardwareAddress(AHandle:THandle;const AAddress:THardwareAddress):Boolean; override;
   function GetBroadcastAddress(AHandle:THandle):THardwareAddress; override;
   function GetMulticastAddresses(AHandle:THandle):TMulticastAddresses; override;

   function AddMulticastAddress(AHandle:THandle;const AAddress:THardwareAddress):Boolean; override;
   function RemoveMulticastAddress(AHandle:THandle;const AAddress:THardwareAddress):Boolean; override;
   
   function StartAdapter:Boolean; override;
   function StopAdapter:Boolean; override;
   function ProcessAdapter:Boolean; override;
 end;
 
 TEAPOLTransportAdapter = class(TTransportAdapter)
   constructor Create;
  private
   {Internal Variables}
   
  public
   {Status Variables}
   
 end;
 
 TEAPOLTransport = class(TNetworkTransport)
   constructor Create(AManager:TTransportManager;const AName:String);
   destructor Destroy; override;
  private
   {Internal Variables}
 
   {Status Variables}

   {Internal Methods}
   function PacketHandler(AHandle:THandle;ASource,ADest,APacket:Pointer;ASize:Integer;ABroadcast:Boolean):Boolean;

  protected
   {Inherited Methods}

   {Internal Methods}
   
  public
   {Public Methods}
   function AddAdapter(AAdapter:TNetworkAdapter;AConfigType:Word;AAddress,ANetmask,AGateway,AServer:Pointer):Boolean; override;
   function RemoveAdapter(AAdapter:TNetworkAdapter):Boolean; override;

   function StartTransport:Boolean; override;
   function StopTransport:Boolean; override;
   function ProcessTransport:Boolean; override;

   function BindTransport(AAdapter:TNetworkAdapter):Boolean; override;
   function UnbindTransport(AAdapter:TNetworkAdapter):Boolean; override;
   
   {EAPOL Methods}
   //To Do
 end;

 TRSNTransportAdapter = class(TTransportAdapter)
   constructor Create;
  private
   {Internal Variables}
   
  public
   {Status Variables}
   
 end;
 
 TRSNTransport = class(TNetworkTransport)
   constructor Create(AManager:TTransportManager;const AName:String);
   destructor Destroy; override;
  private
   {Internal Variables}
 
   {Status Variables}

   {Internal Methods}
   function PacketHandler(AHandle:THandle;ASource,ADest,APacket:Pointer;ASize:Integer;ABroadcast:Boolean):Boolean;

  protected
   {Inherited Methods}

   {Internal Methods}
   
  public
   {Public Methods}
   function AddAdapter(AAdapter:TNetworkAdapter;AConfigType:Word;AAddress,ANetmask,AGateway,AServer:Pointer):Boolean; override;
   function RemoveAdapter(AAdapter:TNetworkAdapter):Boolean; override;

   function StartTransport:Boolean; override;
   function StopTransport:Boolean; override;
   function ProcessTransport:Boolean; override;

   function BindTransport(AAdapter:TNetworkAdapter):Boolean; override;
   function UnbindTransport(AAdapter:TNetworkAdapter):Boolean; override;
   
   {RSN Methods}
   //To Do
 end;
 
{==============================================================================}
{var}
 {WiFi specific variables}
 
{==============================================================================}
{Initialization Functions}
procedure WiFiInit;
function WiFiStart(Data:Pointer;Event:LongWord):LongWord;
function WiFiStop(Data:Pointer;Event:LongWord):LongWord;

function WiFiStartCompleted:Boolean;

{==============================================================================}
{WiFi Functions}
function WiFiDeviceConfigure(WiFi:PWiFiDevice;Flags:LongWord):LongWord;
function WiFiDeviceConfigureFilter(WiFi:PWiFiDevice;var Filter:LongWord):LongWord;
function WiFiDeviceConfigureInterface(WiFi:PWiFiDevice;Interrface:PWiFiInterface):LongWord; 

//To Do //WiFiDeviceSetState/WiFiDeviceSetStatus

function WiFiDeviceCreate:PWiFiDevice;
function WiFiDeviceCreateEx(Size:LongWord):PWiFiDevice;
function WiFiDeviceDestroy(WiFi:PWiFiDevice):LongWord;

function WiFiDeviceRegister(WiFi:PWiFiDevice):LongWord;
function WiFiDeviceDeregister(WiFi:PWiFiDevice):LongWord;

{==============================================================================}
{IEEE80211 Functions}

{==============================================================================}
{WiFi Helper Functions}
function WiFiConfigurationIsHT(Configuration:PWiFiConfiguration):Boolean;
function WiFiConfigurationIsHT20(Configuration:PWiFiConfiguration):Boolean;
function WiFiConfigurationIsHT40(Configuration:PWiFiConfiguration):Boolean;
function WiFiConfigurationIsHT40Plus(Configuration:PWiFiConfiguration):Boolean;
function WiFiConfigurationIsHT40Minus(Configuration:PWiFiConfiguration):Boolean;

procedure WiFiNetworkDeviceAdd(Event:PWiFiDeviceEvent);
function WiFiNetworkDeviceRemove(Network:PNetworkDevice):LongWord;

function WiFiNetworkDeviceEnum(Network:PNetworkDevice;Data:Pointer):LongWord;
function WiFiNetworkDeviceNotify(Device:PDevice;Data:Pointer;Notification:LongWord):LongWord;

{==============================================================================}
{IEEE80211 Helper Functions}
function IEEE80211HasToDS(FrameControl:Word):Boolean; inline;
function IEEE80211HasFromDS(FrameControl:Word):Boolean; inline;
function IEEE80211HasA4(FrameControl:Word):Boolean; inline;

function IEEE80211HasMoreFrags(FrameControl:Word):Boolean; inline;
function IEEE80211HasRetry(FrameControl:Word):Boolean; inline;
function IEEE80211HasPM(FrameControl:Word):Boolean; inline;
function IEEE80211HasMoreData(FrameControl:Word):Boolean; inline;
function IEEE80211HasProtected(FrameControl:Word):Boolean; inline;
function IEEE80211HasOrder(FrameControl:Word):Boolean; inline;

function IEEE80211IsMgmt(FrameControl:Word):Boolean; inline;
function IEEE80211IsCtl(FrameControl:Word):Boolean; inline;
function IEEE80211IsData(FrameControl:Word):Boolean; inline;
function IEEE80211IsDataQoS(FrameControl:Word):Boolean; inline;
function IEEE80211IsDataPresent(FrameControl:Word):Boolean; inline;

function IEEE80211IsAssocReq(FrameControl:Word):Boolean; inline;
function IEEE80211IsAssocResp(FrameControl:Word):Boolean; inline;

function IEEE80211IsReassocReq(FrameControl:Word):Boolean; inline;
function IEEE80211IsReassocResp(FrameControl:Word):Boolean; inline;

function IEEE80211IsProbeReq(FrameControl:Word):Boolean; inline;
function IEEE80211IsProbeResp(FrameControl:Word):Boolean; inline;
function IEEE80211IsBeacon(FrameControl:Word):Boolean; inline;
function IEEE80211IsATIM(FrameControl:Word):Boolean; inline;

function IEEE80211IsDisassoc(FrameControl:Word):Boolean; inline;
function IEEE80211IsAuth(FrameControl:Word):Boolean; inline;
function IEEE80211IsDeauth(FrameControl:Word):Boolean; inline;

function IEEE80211IsAction(FrameControl:Word):Boolean; inline;
function IEEE80211IsBackReq(FrameControl:Word):Boolean; inline;
function IEEE80211IsBack(FrameControl:Word):Boolean; inline;
function IEEE80211IsPSPoll(FrameControl:Word):Boolean; inline;
function IEEE80211IsRTS(FrameControl:Word):Boolean; inline;
function IEEE80211IsCTS(FrameControl:Word):Boolean; inline;
function IEEE80211IsACK(FrameControl:Word):Boolean; inline;

function IEEE80211IsCFEnd(FrameControl:Word):Boolean; inline;
function IEEE80211IsCFEndAck(FrameControl:Word):Boolean; inline;
function IEEE80211IsNullFunc(FrameControl:Word):Boolean; inline;
function IEEE80211IsQoSNullFunc(FrameControl:Word):Boolean; inline;
function IEEE80211IsBufferableMMPDU(FrameControl:Word):Boolean; inline;

function IEEE80211IsFirstFrag(SequenceControl:Word):Boolean; inline;

function IEEE80211HeaderLength(FrameControl:Word):LongWord;
function IEEE80211HeaderLengthFromBuffer(Data:Pointer;Size:LongWord):LongWord;

function IEEE80211MCSToChains(MCS:PIEEE80211MCSInfo):Byte;

function IEEE80211ChannelToFrequency(Channel:Integer;Band:LongWord):Integer;
function IEEE80211FrequencyToChannel(Frequency:Integer):Integer;

procedure IEEE80211InitializeChannelDefinition(Definition:PIEEE80211ChannelDefinition;Channel:PIEEE80211Channel;ChannelType:LongWord);

function IEEE80211FindInformationElement(Identifier:Byte;InformationElement:PByte;ElementLength:LongWord):PByte;

function IEEE80211ParseInformationElements(Buffer:Pointer;Size:LongWord;Action:Boolean;Elements:PIEEE80211InformationElements):Boolean;

{==============================================================================}
{==============================================================================}

implementation

{==============================================================================}
{==============================================================================}
var
 {WiFi specific variables}
 WiFiInitialized:Boolean;
 WiFiStarted:Boolean;
 
{==============================================================================}
{==============================================================================}
{TWiFiAdapter}
constructor TWiFiAdapter.Create(AManager:TAdapterManager;ADevice:PNetworkDevice;const AName:String);
begin
 {}
 inherited Create(AManager,ADevice,AName);
 {Set Defaults}
 FAdapterType:=ADAPTER_TYPE_WIRELESS;
 FillChar(FDefaultAddress,SizeOf(THardwareAddress),0);
 FillChar(FHardwareAddress,SizeOf(THardwareAddress),0);
 FillChar(FBroadcastAddress,SizeOf(THardwareAddress),0);
 FillChar(FMulticastAddresses,SizeOf(TMulticastAddresses),0);
end;

{==============================================================================}

function TWiFiAdapter.CheckReceiveFrame(APacket:PNetworkPacket;AStatus:PIEEE80211RXStatus):Boolean;
{Check if a received frame is valid to continue processing or should be dropped}

{should_drop_frame}
begin
 {}
 Result:=False;
 
 {Check Packet} 
 if APacket = nil then Exit;
 
 {Check Status}
 if AStatus = nil then Exit;
 
 {Check Frame Errors}
 if (AStatus.Flags and (WIFI_RX_FLAG_FAILED_FCS_CRC or WIFI_RX_FLAG_FAILED_PLCP_CRC or WIFI_RX_FLAG_AMPDU_IS_ZEROLEN)) <> 0 then Exit;
 
 {Check Frame Size}
 if APacket.Length < 16 then Exit;
 
 {Check Control Frames}
 if IEEE80211IsCtl(PIEEE80211Header(APacket.Data).FrameControl) and not(IEEE80211IsPSPoll(PIEEE80211Header(APacket.Data).FrameControl)) and not(IEEE80211IsBackReq(PIEEE80211Header(APacket.Data).FrameControl)) then Exit;
 
 Result:=True;
end;
 
{==============================================================================}
 
function TWiFiAdapter.ReceiveHandler(APacket:PNetworkPacket;AStatus:PIEEE80211RXStatus):Boolean;
{__ieee80211_rx_handle_packet / ieee80211_prepare_and_rx_handle}
var
 FrameType:Word;
 PacketType:Word;
 FrameControl:Word;
 Transport:TAdapterTransport; 
begin
 {}
 Result:=False;
 
 {Check Packet} 
 if APacket = nil then Exit;
 
 {Check Status}
 if AStatus = nil then Exit;
 
 {Get Frame Control}
 FrameControl:=PIEEE80211Header(APacket.Data).FrameControl;

 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: Receiving Frame (FrameControl=' + IntToHex(FrameControl,4) + ')');
 {$ENDIF}
 
 {Check Management}
 if IEEE80211IsMgmt(FrameControl) then
  begin
   {Check Length (Drop if too short)}
   if APacket.Length < IEEE80211HeaderLength(FrameControl) then Exit;
  end;
 
 {Check Beacon / Probe Response}
 if IEEE80211IsProbeResp(FrameControl) or IEEE80211IsBeacon(FrameControl) then
  begin
   Result:=ScanReceiver(APacket,AStatus);
  end;
  
 {Check Data}
 if IEEE80211IsData(FrameControl) then
  begin
   Result:=DataReceiver(APacket,AStatus);
  end
 else
  begin 
 
   //To Do //Continuing //ieee80211_prepare_and_rx_handle
   
  end; 
end;
 
{==============================================================================}
 
function TWiFiAdapter.ScanReceiver(APacket:PNetworkPacket;AStatus:PIEEE80211RXStatus):Boolean;
{ieee80211_scan_rx}
var
 Buffer:Pointer;
 Offset:LongWord;
 Management:PIEEE80211Management;
 Elements:TIEEE80211InformationElements;
begin
 {}
 Result:=False;
 
 {Check Packet} 
 if APacket = nil then Exit;
 
 {Check Status}
 if AStatus = nil then Exit;

 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: Scan Receive');
 {$ENDIF}

 {Get Management}
 Management:=PIEEE80211Management(APacket.Data);
 
 {Check Length (Drop if too short)}
 if APacket.Length < 24 then Exit;

 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: FrameControl=' + IntToHex(Management.FrameControl,4));
 if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: Duration=' + IntToHex(Management.Duration,4));
 if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: DestinationAddress=' + HardwareAddressToString(Management.DestinationAddress));
 if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: SourceAddress=' + HardwareAddressToString(Management.SourceAddress));
 if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: BSSID=' + HardwareAddressToString(Management.BSSID));
 if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: SequenceControl=' + IntToHex(Management.SequenceControl,4));
 {$ENDIF}
 
 {Check Type}
 if IEEE80211IsProbeResp(Management.FrameControl) then
  begin
   {Elements Buffer}
   Buffer:=@Management.ProbeResponse.Variable;
   
   {Elements Offset}
   Offset:=PtrUInt(Buffer) - PtrUInt(Management);
   
   {$IFDEF WIFI_DEBUG}
   if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: ProbeResponse.Timestamp=' + IntToHex(Management.ProbeResponse.Timestamp,16));
   if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: ProbeResponse.BeaconInterval=' + IntToHex(Management.ProbeResponse.BeaconInterval,4));
   if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: ProbeResponse.CapabilitiesInfo=' + IntToHex(Management.ProbeResponse.CapabilitiesInfo,4));
   {$ENDIF}
  end
 else if IEEE80211IsBeacon(Management.FrameControl) then 
  begin
   {Elements Buffer}
   Buffer:=@Management.Beacon.Variable;
  
   {Elements Offset}
   Offset:=PtrUInt(Buffer) - PtrUInt(Management);
   
   {$IFDEF WIFI_DEBUG}
   if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: Beacon.Timestamp=' + IntToHex(Management.Beacon.Timestamp,16));
   if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: Beacon.BeaconInterval=' + IntToHex(Management.Beacon.BeaconInterval,4));
   if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: Beacon.CapabilitiesInfo=' + IntToHex(Management.Beacon.CapabilitiesInfo,4));
   {$ENDIF}
  end
 else
  begin
   {Invalid}
   Exit;
  end;  
 
 {Check Offset}
 if Offset > APacket.Length then Exit;
 
 {Parse Elements}
 if not IEEE80211ParseInformationElements(Buffer + Offset,APacket.Length - Offset,False,@Elements) then Exit;
 
 //check Offset ? - Done
 if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: Offset=' + IntToStr(Offset));
 
 //To Do //Continuing //ieee802_11_parse_elems
 
 //To Do //Continuing
 
 Result:=True;
end;
 
{==============================================================================}

function TWiFiAdapter.DataReceiver(APacket:PNetworkPacket;AStatus:PIEEE80211RXStatus):Boolean;
{ieee80211_prepare_and_rx_handle}
begin
 {}
 Result:=False;
 
 {Check Packet} 
 if APacket = nil then Exit;
 
 {Check Status}
 if AStatus = nil then Exit;

 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: Data Receive');
 {$ENDIF}
 
 //To Do //Continuing
end;
 
{==============================================================================}

procedure TWiFiAdapter.SetStatus(AStatus:Integer); 
begin
 {}
 {Check State}
 if FState <> ADAPTER_STATE_ENABLED then Exit;
 
 {Check Status}
 case AStatus of
  ADAPTER_STATUS_DOWN:begin
    {Set Status}
    FStatus:=AStatus;
    
    {$IFDEF NETWORK_DEBUG}
    if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: Status = ADAPTER_STATUS_DOWN');
    {$ENDIF}
   end;
  ADAPTER_STATUS_UP:begin
    {Check Device}
    //To Do //Check Device status before allowing ADAPTER_STATUS_UP
    
    {Set Status}
    FStatus:=AStatus;
    
    {$IFDEF NETWORK_DEBUG}
    if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: Status = ADAPTER_STATUS_UP');
    {$ENDIF}
   end;  
 end;
end;
 
{==============================================================================}

function TWiFiAdapter.AddTransport(APacketType,AFrameType:Word;const APacketName:String;APacketHandler:TAdapterPacketHandler):THandle;
var
 Transport:TAdapterTransport;
begin
 {}
 ReaderLock;
 try
  Result:=INVALID_HANDLE_VALUE;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: AddTransport (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Packet = ' + PacketTypeToString(APacketType));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Frame = ' + FrameTypeToString(AFrameType));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Name = ' + APacketName);
  {$ENDIF}
  
  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;
  
  {Check Device}
  if FDevice = nil then Exit;
  
  {Get Transport}
  Transport:=TAdapterTransport(GetTransportByType(APacketType,AFrameType,False,NETWORK_LOCK_NONE)); {Do not lock}
  if Transport <> nil then Exit;
 
  {Check Frame Type}
  case AFrameType of 
   FRAME_TYPE_ETHERNET_II:begin //To Do //Continuing //FRAME_TYPE_ETHERNET_8022
     {Check Media Type}
     if FMediaType <> MEDIA_TYPE_ETHERNET then Exit;
    end;
   else
    begin
     {Invalid}
     Exit;
    end;
  end;
  
  {Create Transport}
  Transport:=TAdapterTransport.Create;
  Transport.FrameType:=AFrameType;
  Transport.PacketType:=APacketType;
  Transport.PacketName:=APacketName;
  Transport.PacketHandler:=APacketHandler;
 
  {Acquire Lock}
  FTransports.WriterLock;
  try
   {Add Transport}
   FTransports.Add(Transport);
 
   {Return Result}
   Result:=THandle(Transport);
  finally
   {Release Lock}
   FTransports.WriterUnlock;
  end;  
 finally 
  ReaderUnlock;
 end; 
end;
    
{==============================================================================}

function TWiFiAdapter.RemoveTransport(AHandle:THandle;APacketType:Word):Boolean;
var
 Transport:TAdapterTransport;
begin
 {}
 ReaderLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: RemoveTransport (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Packet = ' + PacketTypeToString(APacketType));
  {$ENDIF}

  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;
  
  {Get Transport}
  Transport:=TAdapterTransport(GetTransportByHandle(AHandle,True,NETWORK_LOCK_WRITE)); {Writer due to remove}
  if Transport = nil then Exit;
  
  {Check Transport}
  if Transport.PacketType <> APacketType then
   begin
    {Unlock Transport}
    Transport.WriterUnlock;
    Exit;
   end; 
  
  {Acquire Lock}
  FTransports.WriterLock;
  try
   {Remove Transport}
   FTransports.Remove(Transport);
  
   {Unlock Transport}
   Transport.WriterUnlock;
  
   {Destroy Transport}
   Transport.Free;
 
   {Return Result}
   Result:=True;
  finally
   {Release Lock}
   FTransports.WriterUnlock;
  end;  
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}

function TWiFiAdapter.GetMTU(AHandle:THandle):Word;
var
 Value:PtrUInt;
begin
 {}
 ReaderLock;
 try
  Result:=0;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: GetMTU (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
  {$ENDIF}

  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;

  {Check Device}
  if FDevice = nil then Exit;

  {Get Device MTU}
  if NetworkDeviceControl(FDevice,NETWORK_CONTROL_GET_MTU,0,Value) <> ERROR_SUCCESS then Exit; //To Do
 
  {Return Result}
  Result:=Value;
 finally 
  ReaderUnlock;
 end; 
end;
    
{==============================================================================}

function TWiFiAdapter.SendPacket(AHandle:THandle;ADest:Pointer;APacket:PPacketFragment;ASize:Integer):Boolean;
var
 Size:Integer;
 Length:LongWord;
 Buffer:Pointer;
 Entry:PNetworkEntry;
 Packet:PNetworkPacket;
 Fragment:PPacketFragment;
 Ethernet:PEthernetHeader;
 Transport:TAdapterTransport;
begin
 {}
 Result:=False;
  
 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: SendPacket (' + Name + ')');
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Size = ' + IntToStr(ASize));
 {$ENDIF}
  
 {Check Dest}
 if ADest = nil then Exit;
  
 {Check Packet}
 if APacket = nil then Exit;

 {Check State}
 if FState = ADAPTER_STATE_DISABLED then Exit;
 
 {Check Device}
 if FDevice = nil then Exit;
 
 {Check Flags}
 if (FDevice.Device.DeviceFlags and NETWORK_FLAG_TX_BUFFER) = 0 then Exit;
 
 {Get Transport}
 Transport:=TAdapterTransport(GetTransportByHandle(AHandle,True,NETWORK_LOCK_READ));
 if Transport = nil then Exit;
 try
  //To Do //Continuing
 finally 
  Transport.ReaderUnlock;
 end; 
end;

{==============================================================================}

function TWiFiAdapter.ClearStatistics(AHandle:THandle):Boolean; 
begin
 {}
 ReaderLock;
 try
  Result:=False;

  {$IFDEF NETWORK_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: ClearStatistics (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
  {$ENDIF}

  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;

  {Check Device}
  if FDevice = nil then Exit;
  
  //To Do //NETWORK_CONTROL_CLEAR_STATS
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}

function TWiFiAdapter.GetStatistics(AHandle:THandle):TAdapterStatistics; 
begin
 {}
 ReaderLock;
 try
  FillChar(Result,SizeOf(TAdapterStatistics),0);

  {$IFDEF NETWORK_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: GetStatistics (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
  {$ENDIF}

  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;

  {Check Device}
  if FDevice = nil then Exit;
  
  //To Do //NETWORK_CONTROL_GET_STATS
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}

function TWiFiAdapter.GetDefaultAddress(AHandle:THandle):THardwareAddress; 
begin
 {}
 ReaderLock;
 try
  FillChar(Result,SizeOf(THardwareAddress),0);
 
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: GetDefaultAddress (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
  {$ENDIF}
 
  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;

  {Check Device}
  if FDevice = nil then Exit;
  
  {Return Result}
  Result:=HARDWARE_DEFAULT;
 finally 
  ReaderUnlock;
 end; 
end;
    
{==============================================================================}

function TWiFiAdapter.GetHardwareAddress(AHandle:THandle):THardwareAddress;
var
 Value:PtrUInt;
begin
 {}
 ReaderLock;
 try
  FillChar(Result,SizeOf(THardwareAddress),0);
 
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: GetHardwareAddress (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
  {$ENDIF}

  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;
  
  {Check Device}
  if FDevice = nil then Exit;
  
  {Get Hardware Address}
  NetworkDeviceControl(FDevice,NETWORK_CONTROL_GET_HARDWARE,PtrUInt(@Result),Value);
 finally 
  ReaderUnlock;
 end; 
end;
    
{==============================================================================}

function TWiFiAdapter.SetHardwareAddress(AHandle:THandle;const AAddress:THardwareAddress):Boolean;
var
 Value:PtrUInt;
begin
 {}
 WriterLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: SetHardwareAddress (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Address = ' + HardwareAddressToString(AAddress));
  {$ENDIF}

  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;
  
  {Check Device}
  if FDevice = nil then Exit;
  
  {Set Hardware Address}
  if NetworkDeviceControl(FDevice,NETWORK_CONTROL_SET_MAC,PtrUInt(@AAddress),Value) = ERROR_SUCCESS then
   begin
    FHardwareAddress:=AAddress;
   
    {Return Result}
    Result:=True;
   end;
 finally 
  WriterUnlock;
 end; 
end;
    
{==============================================================================}

function TWiFiAdapter.GetBroadcastAddress(AHandle:THandle):THardwareAddress;
var
 Value:PtrUInt;
begin
 {}
 ReaderLock;
 try
  FillChar(Result,SizeOf(THardwareAddress),0);
 
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: GetBroadcastAddress (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
  {$ENDIF}

  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;

  {Check Device}
  if FDevice = nil then Exit;
  
  {Get Hardware Address}
  NetworkDeviceControl(FDevice,NETWORK_CONTROL_GET_BROADCAST,PtrUInt(@Result),Value);
 finally 
  ReaderUnlock;
 end; 
end;
    
{==============================================================================}

function TWiFiAdapter.GetMulticastAddresses(AHandle:THandle):TMulticastAddresses;
begin
 {}
 ReaderLock;
 try
  FillChar(Result,SizeOf(TMulticastAddresses),0);
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: GetMulticastAddresses (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
  {$ENDIF}

  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;

  {Check Device}
  if FDevice = nil then Exit;
  
  //To Do //NETWORK_CONTROL_GET_MULTICAST
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}

function TWiFiAdapter.AddMulticastAddress(AHandle:THandle;const AAddress:THardwareAddress):Boolean;
begin
 {}
 WriterLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: AddMulticastAddress (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Address = ' + HardwareAddressToString(AAddress));
  {$ENDIF}

  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;

  {Check Device}
  if FDevice = nil then Exit;
  
  //To Do //NETWORK_CONTROL_ADD_MULTICAST
 finally 
  WriterUnlock;
 end; 
end;

{==============================================================================}

function TWiFiAdapter.RemoveMulticastAddress(AHandle:THandle;const AAddress:THardwareAddress):Boolean;
begin
 {}
 WriterLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: RemoveMulticastAddress (' + Name + ')');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Handle = ' + IntToHex(AHandle,8));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter:  Address = ' + HardwareAddressToString(AAddress));
  {$ENDIF}

  {Check State}
  if FState = ADAPTER_STATE_DISABLED then Exit;

  {Check Device}
  if FDevice = nil then Exit;
  
  //To Do //NETWORK_CONTROL_DEL_MULTICAST
 finally 
  WriterUnlock;
 end; 
end;
    
{==============================================================================}

function TWiFiAdapter.StartAdapter:Boolean;
var
 ConfigurationFlags:LongWord;
begin
 {}
 ReaderLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: StartAdapter (' + Name + ')');
  {$ENDIF}
  
  {Check State}
  if FState <> ADAPTER_STATE_DISABLED then Exit;
  
  {Check Device}
  if FDevice = nil then Exit;
   
  {Check Media Type}
  case FDevice.Device.DeviceType of
   NETWORK_TYPE_80211:FMediaType:=MEDIA_TYPE_ETHERNET;
  end;
  if FMediaType = MEDIA_TYPE_UNKNOWN then Exit;   
  
  {Check Flags}
  if (FDevice.Device.DeviceFlags and NETWORK_FLAG_RX_BUFFER) = 0 then Exit;
  if (FDevice.Device.DeviceFlags and NETWORK_FLAG_TX_BUFFER) = 0 then Exit;
  
  //To Do //See ieee80211_do_open for other stuff (IFTYPES etc)
  
  {Open Device}
  if NetworkDeviceOpen(FDevice) = ERROR_SUCCESS then
   begin
    {Default Flags (Configure all)}
    ConfigurationFlags:=LongWord(not(0));
    
    {Copy Permanent Address} //To Do //Check for Address is zero (Default) (To allow specifying from config)
    System.Move(PWiFiDevice(FDevice).PermanentAddress,PWiFiDevice(FDevice).Interrface.Address,SizeOf(THardwareAddress));
   
    {Configure Interface}
    if WiFiDeviceConfigureInterface(PWiFiDevice(FDevice),@PWiFiDevice(FDevice).Interrface) <> ERROR_SUCCESS then
     begin
      NetworkDeviceClose(FDevice);
      Exit;
     end;
    
    {Configure Device}
    if WiFiDeviceConfigure(PWiFiDevice(FDevice),ConfigurationFlags) <> ERROR_SUCCESS then
     begin
      NetworkDeviceClose(FDevice);
      Exit;
     end;
     
    //To Do
    
    {Set State}
    FState:=ADAPTER_STATE_ENABLED;
    
    {Get Properties}
    FDefaultAddress:=GetDefaultAddress(INVALID_HANDLE_VALUE);
    FHardwareAddress:=GetHardwareAddress(INVALID_HANDLE_VALUE);
    FBroadcastAddress:=GetBroadcastAddress(INVALID_HANDLE_VALUE); 
    FMulticastAddresses:=GetMulticastAddresses(INVALID_HANDLE_VALUE); 

    {Set Status (Down until Associated)}
    FStatus:=ADAPTER_STATUS_DOWN;
    
    {Create Thread}
    FThread:=TAdapterThread.Create(Self);
    {FThread.FreeOnTerminate:=True;} {Freed by StopAdapter}
  
    {Start Thread}
    FThread.Start;
  
    {Return Result}
    Result:=True;
   end; 
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}
    
function TWiFiAdapter.StopAdapter:Boolean;
var
 ResultCode:LongWord;
 Current:TAdapterTransport;
 Transport:TAdapterTransport;
begin
 {}
 ReaderLock;
 try
  Result:=False;
   
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFiAdapter: StopAdapter (' + Name + ')');
  {$ENDIF}

  {Check State}
  if FState <> ADAPTER_STATE_ENABLED then Exit;
  
  {Check Device}
  if FDevice = nil then Exit;
  
  {Check Thread}
  if FThread = nil then Exit;
    
  //To Do //See ieee80211_do_stop
  
  {Close Device}
  ResultCode:=NetworkDeviceClose(FDevice);
  if (ResultCode = ERROR_SUCCESS) or (ResultCode = ERROR_NOT_OPEN) then
   begin
    {Terminate Thread}
    FThread.Terminate;
  
    {Wait For Thread}
    FThread.WaitFor;
  
    {Destroy Thread}
    FThread.Free;
    FThread:=nil;
  
    {Get Transport}
    Transport:=TAdapterTransport(GetTransportByNext(nil,True,False,NETWORK_LOCK_READ));
    while Transport <> nil do
     begin
      {Get Next}
      Current:=Transport;
      Transport:=TAdapterTransport(GetTransportByNext(Current,True,True,NETWORK_LOCK_READ));
    
      {Remove Transport}
      RemoveTransport(THandle(Current),Current.PacketType);
     end;
  
    {Reset Status}
    FStatus:=ADAPTER_STATUS_DOWN;
  
    {Reset Properties}
    FillChar(FDefaultAddress,SizeOf(THardwareAddress),0);
    FillChar(FHardwareAddress,SizeOf(THardwareAddress),0);
    FillChar(FBroadcastAddress,SizeOf(THardwareAddress),0);
    FillChar(FMulticastAddresses,SizeOf(TMulticastAddresses),0);

    {Reset State}
    FState:=ADAPTER_STATE_DISABLED;
    
    {Return Result}
    Result:=True;
   end; 
 finally 
  ReaderUnlock;
 end; 
end;
    
{==============================================================================}

function TWiFiAdapter.ProcessAdapter:Boolean;
{ieee80211_rx}
var
 Rate:PIEEE80211Rate;
 Entry:PNetworkEntry;
 Packet:PNetworkPacket;
 Status:PIEEE80211RXStatus;
 SupportedBand:PIEEE80211SupportedBand;
begin
 {}
 Result:=False;
 
 {Check State}
 if FState = ADAPTER_STATE_DISABLED then Exit;

 {Check Device}
 if FDevice = nil then Exit;
 
 {Check Thread}
 if FThread = nil then Exit;

 {Check Flags}
 if (FDevice.Device.DeviceFlags and NETWORK_FLAG_RX_BUFFER) = 0 then Exit;
 
 {Receive Buffer}
 if NetworkBufferReceive(FDevice,Entry) = ERROR_SUCCESS then
  begin
   try
    {$IFDEF WIFI_DEBUG}
    if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: ProcessAdapter (' + Name + ')');
    {$ENDIF}
 
    {Get Packet}
    Packet:=@Entry.Packets[0]; //To Do //Multiple packets per entry
    
    {$IFDEF WIFI_DEBUG}
    if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: Packet Length = ' + IntToStr(Packet.Length));
    {$ENDIF}
    
    {Get Status}
    Status:=PIEEE80211RXStatus(Entry.DriverData);
    
    {$IFDEF WIFI_DEBUG}
    if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: Status.Band = ' + IntToStr(Status.Band));
    {$ENDIF}
    
    {Check Band}
    if Status.Band >= IEEE80211_NUM_BANDS then Exit;
    
    {Get Band}
    SupportedBand:=PWiFiDevice(FDevice).Bands[Status.Band];
    if SupportedBand = nil then Exit;
    
    //To Do //Continuing //Check Reconfig/Quiescing/Suspended ? //ieee80211_rx
    
    {Check Status}
    Rate:=nil;
    if (Status.Flags and WIFI_RX_FLAG_FAILED_PLCP_CRC) = 0 then
     begin
      {Validate the rate, unless a PLCP error means that we probably can't have a valid rate here anyway}
      if (Status.Flags and WIFI_RX_FLAG_HT) <> 0 then
       begin
        {RateIndex is MCS index, which can be [0-76] as documented on:
        
         http://wireless.kernel.org/en/developers/Documentation/ieee80211/802.11n
        
         Anything else would be some sort of driver or hardware error}
        if Status.RateIndex > 76 then
         begin        
          if NETWORK_LOG_ENABLED then NetworkLogError(FDevice,'WiFiAdapter: HT rate not in MCS index range [0-76] (RateIndex= ' + IntToStr(Status.RateIndex) + ')');
          Exit;
         end;
       end
      else if (Status.Flags and WIFI_RX_FLAG_VHT) <> 0 then 
       begin
        if (Status.RateIndex > 9) or (Status.VHTNSS = 0) or (Status.VHTNSS > 8) then
         begin
          if NETWORK_LOG_ENABLED then NetworkLogError(FDevice,'WiFiAdapter: VHT rate with invalid data (RateIndex= ' + IntToStr(Status.RateIndex) + ' VHTNSS=' + IntToStr(Status.VHTNSS) + ')');
          Exit;
         end;
       end
      else
       begin
        if Status.RateIndex >= SupportedBand.RateCount then
         begin
          if NETWORK_LOG_ENABLED then NetworkLogError(FDevice,'WiFiAdapter: Rate index exceeds supported rate count (RateIndex= ' + IntToStr(Status.RateIndex) + ')');
          Exit;
         end;
        
        {Get Rate}
        Rate:=@SupportedBand.Rates[Status.RateIndex];
       end;       
     end;
    
    //To Do //Continuing //ieee80211_rx_monitor //radiotap not supported yet //Add TAdapterMonitor class
    
    if CheckReceiveFrame(Packet,Status) then
     begin
      //To Do //Continuing //ieee80211_tpt_led_trig_rx
      
      {Call Receive Handler}
      ReceiveHandler(Packet,Status);
     end
    else
     begin
      {$IFDEF WIFI_DEBUG}
      if NETWORK_LOG_ENABLED then NetworkLogDebug(FDevice,'WiFiAdapter: Dropping received frame (FrameControl=' + IntToHex(PIEEE80211Header(Packet.Data).FrameControl,4) + ' Length=' + IntToStr(Packet.Length) +')');
      {$ENDIF}
     end;
     
    {Return Result}
    Result:=True;
   finally
    {Release Buffer}
    NetworkBufferRelease(FDevice,Entry);
   end;
  end; 
end;
 
{==============================================================================}
{==============================================================================}
{TEAPOLTransportAdapter}
constructor TEAPOLTransportAdapter.Create;
begin
 {}
 inherited Create;
end;
 
{==============================================================================}
{==============================================================================}
{TEAPOLTransport}
constructor TEAPOLTransport.Create(AManager:TTransportManager;const AName:String);
begin
 {}
 inherited Create(AManager,AName);
 FFamily:=AF_UNSPEC;
 FPacketType:=PACKET_TYPE_EAPOL;

 //To Do
end;

{==============================================================================}

destructor TEAPOLTransport.Destroy; 
begin
 {}
 WriterLock;
 try
  //To Do
 finally
  {WriterUnlock;} {Can destroy Synchronizer while holding lock}
  inherited Destroy;
 end; 
end;

{==============================================================================}

function TEAPOLTransport.PacketHandler(AHandle:THandle;ASource,ADest,APacket:Pointer;ASize:Integer;ABroadcast:Boolean):Boolean;
{Process a packet received by an Adapter}
{Handle: The Handle of the Transport Adapter the packet was received from}
{Source: The source hardware address of the received packet (Set by Adapter)}
{Dest: The destination hardware address of the received packet (Set by Adapter)}
{Packet: The received packet (The complete packet without Adapter header)}
{Size: The size of the received packet in bytes}
{Broadcast: True if the destination address is a broadcast address}
var
 Adapter:TEAPOLTransportAdapter;
begin
 {}
 Result:=False;
 
 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL: PacketHandler');
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL:  Size = ' + IntToStr(ASize));
 {$ENDIF}
 
 {Get Adapter}
 Adapter:=TEAPOLTransportAdapter(GetAdapterByHandle(AHandle,True,NETWORK_LOCK_READ));
 if Adapter = nil then Exit;
 try
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL:  PacketType = ' + PacketTypeToString(Adapter.PacketType));
  {$ENDIF}
 
  {Check Packet Type}
  case Adapter.PacketType of
   PACKET_TYPE_EAPOL:begin
    
     //To Do

    end;
  end;
 finally 
  Adapter.ReaderUnlock;
 end; 
end;
 
{==============================================================================}

function TEAPOLTransport.AddAdapter(AAdapter:TNetworkAdapter;AConfigType:Word;AAddress,ANetmask,AGateway,AServer:Pointer):Boolean;
{Add an adapter to this transport}
{Adapter: The adapter to add}
{ConfigType: The configuration type to use for configuring the adapter (eg CONFIG_TYPE_AUTO)}
{Address: The transport address to use for this adapter (or nil if supplied during configuration)}
{Netmask: The transport netmask to use for this adapter (or nil if supplied during configuration)}
{Gateway: The transport default gateway to use for this adapter (or nil if supplied during configuration)}
{Server: The transport configuration server to use for this adapter (or nil if supplied during configuration)}
var
 Handle:THandle;
 Adapter:TEAPOLTransportAdapter;
begin
 {}
 ReaderLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL: AddAdapter');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL:  Config = ' + ConfigTypeToString(AConfigType));
  {$ENDIF}
  
  {Check Adapter}
  if AAdapter = nil then Exit;

  {Check State}
  if AAdapter.State <> ADAPTER_STATE_ENABLED then Exit;
  
  {Get Adapter}
  Adapter:=TEAPOLTransportAdapter(GetAdapterByAdapter(AAdapter,True,NETWORK_LOCK_READ));
  if Adapter = nil then
   begin
    {Add EAPOL Type}
    Handle:=AAdapter.AddTransport(PACKET_TYPE_EAPOL,FRAME_TYPE_ETHERNET_II,EAPOL_TRANSPORT_NAME,PacketHandler);
    if Handle <> INVALID_HANDLE_VALUE then
     begin
      {Create Adapter}
      Adapter:=TEAPOLTransportAdapter.Create;
      Adapter.Name:=AAdapter.Name;
      Adapter.Handle:=Handle;
      Adapter.PacketType:=PACKET_TYPE_EAPOL;
      Adapter.Adapter:=AAdapter;
      Adapter.Hardware:=AAdapter.GetHardwareAddress(Handle);
      Adapter.Broadcast:=AAdapter.GetBroadcastAddress(Handle);
      Adapter.MTU:=AAdapter.GetMTU(Handle);
      Adapter.ConfigType:=CONFIG_TYPE_AUTO;
      Adapter.Configured:=True;
      Adapter.Configuring:=False;
      
      {Acquire Lock}
      FAdapters.WriterLock;
      try
       {Add Adapter}
       FAdapters.Add(Adapter);
      
       {Return Result}
       Result:=True;
      finally
       {Release Lock}
       FAdapters.WriterUnlock;
      end;  
     end;
   end
  else
   begin
    {Unlock Adapter}
    Adapter.ReaderUnlock;
    
    {Return Result}
    Result:=True;
   end;
 finally 
  ReaderUnlock;
 end; 
end;
 
{==============================================================================}

function TEAPOLTransport.RemoveAdapter(AAdapter:TNetworkAdapter):Boolean;
{Remove an adapter from this transport}
{Adapter: The adapter to remove}
var
 Adapter:TEAPOLTransportAdapter;
begin
 {}
 ReaderLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL: RemoveAdapter');
  {$ENDIF}
  
  {Check Adapter}
  if AAdapter = nil then Exit;
 
  {Get Adapter}
  Adapter:=TEAPOLTransportAdapter(GetAdapterByAdapter(AAdapter,True,NETWORK_LOCK_WRITE)); {Writer due to remove}
  if Adapter = nil then Exit;
  
  {Remove EAPOL Type}
  if AAdapter.RemoveTransport(Adapter.Handle,Adapter.PacketType) then
   begin
    {Acquire Lock}
    FAdapters.WriterLock;
    try
     {Remove Adapter}
     FAdapters.Remove(Adapter);
    
     {Unlock Adapter}
     Adapter.WriterUnlock;
    
     {Destroy Adapter}
     Adapter.Free;
     
     {Return Result}
     Result:=True;
    finally
     {Release Lock}
     FAdapters.WriterUnlock;
    end;  
   end;
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}

function TEAPOLTransport.StartTransport:Boolean;
{Start this transport ready for sending and receiving}
begin
 {}
 ReaderLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL: StartTransport');
  {$ENDIF}
  
  {Check Manager}
  if Manager = nil then Exit;
  
  {Return Result}
  Result:=True;
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}

function TEAPOLTransport.StopTransport:Boolean;
{Stop this transport ready for removal}
var
 Current:TEAPOLTransportAdapter;
 Adapter:TEAPOLTransportAdapter;
begin
 {}
 ReaderLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL: StopTransport');
  {$ENDIF}
  
  {Check Manager}
  if Manager = nil then Exit;
  
  {Get Adapter}
  Adapter:=TEAPOLTransportAdapter(GetAdapterByNext(nil,True,False,NETWORK_LOCK_READ));
  while Adapter <> nil do
   begin
    {Get Next}
    Current:=Adapter;
    Adapter:=TEAPOLTransportAdapter(GetAdapterByNext(Current,True,True,NETWORK_LOCK_READ));
    
    {Remove Adapter} 
    RemoveAdapter(Current.Adapter); 
   end;
  
  {Return Result}
  Result:=True;
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}

function TEAPOLTransport.ProcessTransport:Boolean;
{Process periodic tasks for this transport}
begin
 {}
 //To Do
 
 {Return Result}
 Result:=True;
end;

{==============================================================================}

function TEAPOLTransport.BindTransport(AAdapter:TNetworkAdapter):Boolean; 
{Bind this transport to an adapter if appropriate}
{Adapter: The adapter to bind to}
var
 Adapter:TEAPOLTransportAdapter;
begin
 {}
 ReaderLock;
 try
  Result:=False; 
 
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL: BindTransport');
  {$ENDIF}
 
  {Check Adapter}
  if AAdapter = nil then Exit;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL:  Adapter = ' + AAdapter.Name);
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL:   State = ' + AdapterStateToString(AAdapter.State));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL:   Status = ' + AdapterStatusToString(AAdapter.Status));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL:   Type = ' + AdapterTypeToString(AAdapter.AdapterType));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL:   Media = ' + MediaTypeToString(AAdapter.MediaType));
  {$ENDIF}

  {Check State}
  if AAdapter.State <> ADAPTER_STATE_ENABLED then Exit;
  
  Result:=True;
  
  {Check Media}
  if AAdapter.MediaType <> MEDIA_TYPE_ETHERNET then Exit;
  
  {Check Type}
  if AAdapter.AdapterType = ADAPTER_TYPE_UNKNOWN then Exit;
  
  Result:=False; 
  
  {Get Adapter}
  Adapter:=TEAPOLTransportAdapter(GetAdapterByAdapter(AAdapter,True,NETWORK_LOCK_READ));
  if Adapter = nil then
   begin
    {Check Type}
    case AAdapter.AdapterType of
     ADAPTER_TYPE_WIRELESS:begin 
       {Add Adapter}
       Result:=AddAdapter(AAdapter,CONFIG_TYPE_AUTO,nil,nil,nil,nil);
      end;     
    end;
   end
  else
   begin
    {Unlock Adapter}
    Adapter.ReaderUnlock;
    
    {Return Result}
    Result:=True;
   end;   
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}

function TEAPOLTransport.UnbindTransport(AAdapter:TNetworkAdapter):Boolean; 
{Unbind this transport from an adapter if appropriate}
{Adapter: The adapter to unbind from}
var
 Adapter:TEAPOLTransportAdapter;
begin
 {}
 ReaderLock;
 try
  Result:=False; 
 
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL: UnbindTransport');
  {$ENDIF}
 
  {Check Adapter}
  if AAdapter = nil then Exit;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'EAPOL:  Adapter = ' + AAdapter.Name);
  {$ENDIF}

  {Get Adapter}
  Adapter:=TEAPOLTransportAdapter(GetAdapterByAdapter(AAdapter,True,NETWORK_LOCK_READ));
  if Adapter <> nil then
   begin
    {Unlock Adapter}
    Adapter.ReaderUnlock;

    {Remove Adapter}
    Result:=RemoveAdapter(AAdapter);
   end
  else
   begin
    {Return Result}
    Result:=True;
   end;   
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}
{==============================================================================}
{TRSNTransportAdapter}
constructor TRSNTransportAdapter.Create;
begin
 {}
 inherited Create;
end;

{==============================================================================}
{==============================================================================}
{TRSNTransport}
constructor TRSNTransport.Create(AManager:TTransportManager;const AName:String);
begin
 {}
 inherited Create(AManager,AName);
 FFamily:=AF_UNSPEC;
 FPacketType:=PACKET_TYPE_RSN;

 //To Do
end;

{==============================================================================}

destructor TRSNTransport.Destroy; 
begin
 {}
 WriterLock;
 try
  //To Do
 finally
  {WriterUnlock;} {Can destroy Synchronizer while holding lock}
  inherited Destroy;
 end; 
end;

{==============================================================================}

function TRSNTransport.PacketHandler(AHandle:THandle;ASource,ADest,APacket:Pointer;ASize:Integer;ABroadcast:Boolean):Boolean;
{Process a packet received by an Adapter}
{Handle: The Handle of the Transport Adapter the packet was received from}
{Source: The source hardware address of the received packet (Set by Adapter)}
{Dest: The destination hardware address of the received packet (Set by Adapter)}
{Packet: The received packet (The complete packet without Adapter header)}
{Size: The size of the received packet in bytes}
{Broadcast: True if the destination address is a broadcast address}
var
 Adapter:TRSNTransportAdapter;
begin
 {}
 Result:=False;
 
 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN: PacketHandler');
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN:  Size = ' + IntToStr(ASize));
 {$ENDIF}
 
 {Get Adapter}
 Adapter:=TRSNTransportAdapter(GetAdapterByHandle(AHandle,True,NETWORK_LOCK_READ));
 if Adapter = nil then Exit;
 try
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN:  PacketType = ' + PacketTypeToString(Adapter.PacketType));
  {$ENDIF}
 
  {Check Packet Type}
  case Adapter.PacketType of
   PACKET_TYPE_RSN:begin
    
     //To Do

    end;
  end;
 finally 
  Adapter.ReaderUnlock;
 end; 
end;
 
{==============================================================================}

function TRSNTransport.AddAdapter(AAdapter:TNetworkAdapter;AConfigType:Word;AAddress,ANetmask,AGateway,AServer:Pointer):Boolean;
{Add an adapter to this transport}
{Adapter: The adapter to add}
{ConfigType: The configuration type to use for configuring the adapter (eg CONFIG_TYPE_AUTO)}
{Address: The transport address to use for this adapter (or nil if supplied during configuration)}
{Netmask: The transport netmask to use for this adapter (or nil if supplied during configuration)}
{Gateway: The transport default gateway to use for this adapter (or nil if supplied during configuration)}
{Server: The transport configuration server to use for this adapter (or nil if supplied during configuration)}
var
 Handle:THandle;
 Adapter:TRSNTransportAdapter;
begin
 {}
 ReaderLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN: AddAdapter');
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN:  Config = ' + ConfigTypeToString(AConfigType));
  {$ENDIF}
  
  {Check Adapter}
  if AAdapter = nil then Exit;

  {Check State}
  if AAdapter.State <> ADAPTER_STATE_ENABLED then Exit;
  
  {Get Adapter}
  Adapter:=TRSNTransportAdapter(GetAdapterByAdapter(AAdapter,True,NETWORK_LOCK_READ));
  if Adapter = nil then
   begin
    {Add RSN Type}
    Handle:=AAdapter.AddTransport(PACKET_TYPE_RSN,FRAME_TYPE_ETHERNET_II,RSN_TRANSPORT_NAME,PacketHandler);
    if Handle <> INVALID_HANDLE_VALUE then
     begin
      {Create Adapter}
      Adapter:=TRSNTransportAdapter.Create;
      Adapter.Name:=AAdapter.Name;
      Adapter.Handle:=Handle;
      Adapter.PacketType:=PACKET_TYPE_RSN;
      Adapter.Adapter:=AAdapter;
      Adapter.Hardware:=AAdapter.GetHardwareAddress(Handle);
      Adapter.Broadcast:=AAdapter.GetBroadcastAddress(Handle);
      Adapter.MTU:=AAdapter.GetMTU(Handle);
      Adapter.ConfigType:=CONFIG_TYPE_AUTO;
      Adapter.Configured:=True;
      Adapter.Configuring:=False;
      
      {Acquire Lock}
      FAdapters.WriterLock;
      try
       {Add Adapter}
       FAdapters.Add(Adapter);
      
       {Return Result}
       Result:=True;
      finally
       {Release Lock}
       FAdapters.WriterUnlock;
      end;  
     end;
   end
  else
   begin
    {Unlock Adapter}
    Adapter.ReaderUnlock;
    
    {Return Result}
    Result:=True;
   end;
 finally 
  ReaderUnlock;
 end; 
end;
 
{==============================================================================}

function TRSNTransport.RemoveAdapter(AAdapter:TNetworkAdapter):Boolean;
{Remove an adapter from this transport}
{Adapter: The adapter to remove}
var
 Adapter:TRSNTransportAdapter;
begin
 {}
 ReaderLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN: RemoveAdapter');
  {$ENDIF}
  
  {Check Adapter}
  if AAdapter = nil then Exit;
 
  {Get Adapter}
  Adapter:=TRSNTransportAdapter(GetAdapterByAdapter(AAdapter,True,NETWORK_LOCK_WRITE)); {Writer due to remove}
  if Adapter = nil then Exit;
  
  {Remove RSN Type}
  if AAdapter.RemoveTransport(Adapter.Handle,Adapter.PacketType) then
   begin
    {Acquire Lock}
    FAdapters.WriterLock;
    try
     {Remove Adapter}
     FAdapters.Remove(Adapter);
    
     {Unlock Adapter}
     Adapter.WriterUnlock;
    
     {Destroy Adapter}
     Adapter.Free;
     
     {Return Result}
     Result:=True;
    finally
     {Release Lock}
     FAdapters.WriterUnlock;
    end;  
   end;
 finally 
  ReaderUnlock;
 end; 
end;
 
{==============================================================================}

function TRSNTransport.StartTransport:Boolean;
{Start this transport ready for sending and receiving}
begin
 {}
 ReaderLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN: StartTransport');
  {$ENDIF}
  
  {Check Manager}
  if Manager = nil then Exit;
  
  {Return Result}
  Result:=True;
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}

function TRSNTransport.StopTransport:Boolean;
{Stop this transport ready for removal}
var
 Current:TRSNTransportAdapter;
 Adapter:TRSNTransportAdapter;
begin
 {}
 ReaderLock;
 try
  Result:=False;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN: StopTransport');
  {$ENDIF}
  
  {Check Manager}
  if Manager = nil then Exit;
  
  {Get Adapter}
  Adapter:=TRSNTransportAdapter(GetAdapterByNext(nil,True,False,NETWORK_LOCK_READ));
  while Adapter <> nil do
   begin
    {Get Next}
    Current:=Adapter;
    Adapter:=TRSNTransportAdapter(GetAdapterByNext(Current,True,True,NETWORK_LOCK_READ));
    
    {Remove Adapter} 
    RemoveAdapter(Current.Adapter);
   end;
  
  {Return Result}
  Result:=True;
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}

function TRSNTransport.ProcessTransport:Boolean;
{Process periodic tasks for this transport}
begin
 {}
 //To Do
 
 {Return Result}
 Result:=True;
end;

{==============================================================================}

function TRSNTransport.BindTransport(AAdapter:TNetworkAdapter):Boolean; 
{Bind this transport to an adapter if appropriate}
{Adapter: The adapter to bind to}
var
 Adapter:TRSNTransportAdapter;
begin
 {}
 ReaderLock;
 try
  Result:=False; 
 
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN: BindTransport');
  {$ENDIF}
 
  {Check Adapter}
  if AAdapter = nil then Exit;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN:  Adapter = ' + AAdapter.Name);
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN:   State = ' + AdapterStateToString(AAdapter.State));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN:   Status = ' + AdapterStatusToString(AAdapter.Status));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN:   Type = ' + AdapterTypeToString(AAdapter.AdapterType));
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN:   Media = ' + MediaTypeToString(AAdapter.MediaType));
  {$ENDIF}

  {Check State}
  if AAdapter.State <> ADAPTER_STATE_ENABLED then Exit;
  
  Result:=True;
  
  {Check Media}
  if AAdapter.MediaType <> MEDIA_TYPE_ETHERNET then Exit;
  
  {Check Type}
  if AAdapter.AdapterType = ADAPTER_TYPE_UNKNOWN then Exit;
  
  Result:=False; 
  
  {Get Adapter}
  Adapter:=TRSNTransportAdapter(GetAdapterByAdapter(AAdapter,True,NETWORK_LOCK_READ));
  if Adapter = nil then
   begin
    {Check Type}
    case AAdapter.AdapterType of
     ADAPTER_TYPE_WIRELESS:begin 
       {Add Adapter}
       Result:=AddAdapter(AAdapter,CONFIG_TYPE_AUTO,nil,nil,nil,nil);
      end;     
    end;
   end
  else
   begin
    {Unlock Adapter}
    Adapter.ReaderUnlock;
    
    {Return Result}
    Result:=True;
   end;   
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}

function TRSNTransport.UnbindTransport(AAdapter:TNetworkAdapter):Boolean; 
{Unbind this transport from an adapter if appropriate}
{Adapter: The adapter to unbind from}
var
 Adapter:TRSNTransportAdapter;
begin
 {}
 ReaderLock;
 try
  Result:=False; 
 
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN: UnbindTransport');
  {$ENDIF}
 
  {Check Adapter}
  if AAdapter = nil then Exit;
  
  {$IFDEF WIFI_DEBUG}
  if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'RSN:  Adapter = ' + AAdapter.Name);
  {$ENDIF}

  {Get Adapter}
  Adapter:=TRSNTransportAdapter(GetAdapterByAdapter(AAdapter,True,NETWORK_LOCK_READ));
  if Adapter <> nil then
   begin
    {Unlock Adapter}
    Adapter.ReaderUnlock;

    {Remove Adapter}
    Result:=RemoveAdapter(AAdapter);
   end
  else
   begin
    {Return Result}
    Result:=True;
   end;   
 finally 
  ReaderUnlock;
 end; 
end;

{==============================================================================}
{==============================================================================}
{Initialization Functions}
procedure WiFiInit;
begin
 {}
 {Check Initialized}
 if WiFiInitialized then Exit;
 
 {Create EAPOL Transport}
 if NetworkSettings.GetBooleanDefault('EAPOL_TRANSPORT_ENABLED',EAPOL_TRANSPORT_ENABLED) then 
  begin
   TEAPOLTransport.Create(TransportManager,EAPOL_TRANSPORT_NAME);
  end; 
 
 {Create RSN Transport}
 if NetworkSettings.GetBooleanDefault('RSN_TRANSPORT_ENABLED',RSN_TRANSPORT_ENABLED) then 
  begin
   TRSNTransport.Create(TransportManager,RSN_TRANSPORT_NAME);
  end; 
 
 {Register Start Event}
 NetworkEventRegister(WiFiStart,nil,NETWORK_EVENT_SYSTEM_START);
 
 {Register Stop Event}
 NetworkEventRegister(WiFiStop,nil,NETWORK_EVENT_SYSTEM_STOP);
 
 WiFiInitialized:=True;
end;

{==============================================================================}

function WiFiStart(Data:Pointer;Event:LongWord):LongWord;
begin
 {}
 Result:=ERROR_SUCCESS;
 
 {Check Started}
 if WiFiStarted then Exit;
 
 {Check Event}
 if (Event and NETWORK_EVENT_SYSTEM_START) <> 0 then
  begin
   Result:=ERROR_INVALID_PARAMETER;
   
   {Register Notification}
   NetworkDeviceNotification(nil,WiFiNetworkDeviceNotify,nil,DEVICE_NOTIFICATION_REGISTER or DEVICE_NOTIFICATION_DEREGISTER or DEVICE_NOTIFICATION_CLOSING,NOTIFIER_FLAG_NONE);
   
   {Enumerate Adapters}
   NetworkDeviceEnumerate(WiFiNetworkDeviceEnum,nil);
   
   {Set Started} 
   WiFiStarted:=True;
   
   {Return Result} 
   Result:=ERROR_SUCCESS;
  end; 
end;

{==============================================================================}

function WiFiStop(Data:Pointer;Event:LongWord):LongWord;
begin
 {}
 Result:=ERROR_SUCCESS;
 
 {Check Started}
 if not(WiFiStarted) then Exit;
 
 {Check Event}
 if (Event and NETWORK_EVENT_SYSTEM_STOP) <> 0 then
  begin
   Result:=ERROR_INVALID_PARAMETER;
   
   {Deregister Notification}
   NetworkDeviceNotification(nil,WiFiNetworkDeviceNotify,nil,DEVICE_NOTIFICATION_NONE,NOTIFIER_FLAG_NONE);
   
   {Set Started}
   WiFiStarted:=False;    
   
   {Return Result} 
   Result:=ERROR_SUCCESS;
  end; 
end;
 
{==============================================================================}
 
function WiFiStartCompleted:Boolean;
{Returns True if the WiFi sub system has been started}
begin
 {}
 Result:=WiFiStarted;
end;
 
{==============================================================================}
{==============================================================================}
{WiFi Functions}
function WiFiDeviceConfigure(WiFi:PWiFiDevice;Flags:LongWord):LongWord;
begin
 {}
 Result:=ERROR_INVALID_PARAMETER;

 {Check WiFi}
 if WiFi = nil then Exit;
 if WiFi.Network.Device.Signature <> DEVICE_SIGNATURE then Exit;
 
 {Check Method}
 if not Assigned(WiFi.DeviceConfigure) then Exit;
 
 //To Do //Check for channel/power etc changes //See ieee80211_hw_conf_chan (do within this function, not used elsewhere)
 
 {Call Configure}
 Result:=WiFi.DeviceConfigure(WiFi,Flags);
end;

{==============================================================================}

function WiFiDeviceConfigureFilter(WiFi:PWiFiDevice;var Filter:LongWord):LongWord;
begin
 {}
 Result:=ERROR_INVALID_PARAMETER;

 {Check WiFi}
 if WiFi = nil then Exit;
 if WiFi.Network.Device.Signature <> DEVICE_SIGNATURE then Exit;
 
 {Check Method}
 if not Assigned(WiFi.DeviceConfigureFilter) then Exit;
 
 {Call Configure Filter}
 Result:=WiFi.DeviceConfigureFilter(WiFi,Filter);
end;

{==============================================================================}

function WiFiDeviceConfigureInterface(WiFi:PWiFiDevice;Interrface:PWiFiInterface):LongWord; 
begin
 {}
 Result:=ERROR_INVALID_PARAMETER;

 {Check WiFi}
 if WiFi = nil then Exit;
 if WiFi.Network.Device.Signature <> DEVICE_SIGNATURE then Exit;
 
 {Check Method}
 if not Assigned(WiFi.DeviceConfigureInterface) then Exit;
 
 {Call Configure Interface}
 Result:=WiFi.DeviceConfigureInterface(WiFi,Interrface);
end;

{==============================================================================}

function WiFiDeviceCreate:PWiFiDevice;
{Create a new WiFi entry}
{Return: Pointer to new WiFi entry or nil if entry could not be created}
begin
 {}
 Result:=WiFiDeviceCreateEx(SizeOf(TWiFiDevice));
end;

{==============================================================================}

function WiFiDeviceCreateEx(Size:LongWord):PWiFiDevice;
{Create a new WiFi entry}
{Size: Size in bytes to allocate for new entry (Including the WiFi entry)}
{Return: Pointer to new WiFi entry or nil if entry could not be created}
begin
 {}
 {Create WiFi}
 Result:=PWiFiDevice(NetworkDeviceCreateEx(Size));
 if Result = nil then Exit;
 
 {Update WiFi}
 Result.WiFiFlags:=WIFI_FLAG_NETNS_OK or WIFI_FLAG_4ADDR_AP or WIFI_FLAG_4ADDR_STATION or WIFI_FLAG_REPORTS_OBSS or WIFI_FLAG_OFFCHAN_TX;
 Result.WiFiState:=WIFI_STATE_NONE;
 Result.WiFiStatus:=WIFI_STATUS_NONE;
 Result.WiFiFeatures:=WIFI_FEATURE_SK_TX_STATUS or WIFI_FEATURE_SAE or WIFI_FEATURE_HT_IBSS or WIFI_FEATURE_VIF_TXPOWER or WIFI_FEATURE_MAC_ON_CREATE or WIFI_FEATURE_USERSPACE_MPM;
 Result.DeviceConfigure:=nil;
 Result.DeviceConfigureFilter:=nil;
 Result.DeviceConfigureInterface:=nil;
 Result.RetryShort:=7;
 Result.RetryLong:=4;
 Result.FragThreshold:=LongWord(-1);
 Result.RTSThreshold:=LongWord(-1);
 Result.CoverageClass:=0;
 Result.MaxCSACounters:=1;
 {Hardware}
 Result.Hardware.MaxRates:=1;
 Result.Hardware.MaxReportRates:=0;
 Result.Hardware.MaxRXAggregationSubframes:=IEEE80211_MAX_AMPDU_BUF;
 Result.Hardware.MaxTXAggregationSubframes:=IEEE80211_MAX_AMPDU_BUF;
 {Configuration}
 Result.Configuration.LongFrameMaxTXCount:=Result.RetryLong;
 Result.Configuration.ShortFrameMaxTXCount:=Result.RetryShort;
 
 
 //To Do //ieee80211_alloc_hw_nm
 
 //To Do //ieee80211_default_mgmt_stypes//
end;

{==============================================================================}

function WiFiDeviceDestroy(WiFi:PWiFiDevice):LongWord;
begin
 {}
 //To Do //ieee80211_free_hw
 
 //To Do //Free .Bands[]  //.Channels/.Rates (Allocated by driver)
 
 {Destroy Network}
 Result:=NetworkDeviceDestroy(@WiFi.Network);
end;

{==============================================================================}

function WiFiDeviceRegister(WiFi:PWiFiDevice):LongWord;
var
 Band:LongWord;
 MaxRateCount:LongWord;
 ChannelCount:LongWord;
 HTSupported:Boolean;
 VHTSupported:Boolean;
 SupportedBand:PIEEE80211SupportedBand;
 ChannelDefinition:TIEEE80211ChannelDefinition;
begin
 {}
 Result:=ERROR_INVALID_PARAMETER;

 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFi: Device register');
 {$ENDIF}
 
 {Check WiFi}
 if WiFi = nil then Exit;
 if WiFi.Network.Device.Signature <> DEVICE_SIGNATURE then Exit;

 {Check WiFi Functions}
 if not Assigned(WiFi.DeviceConfigure) then Exit;
 if not Assigned(WiFi.DeviceConfigureFilter) then Exit;
 if not Assigned(WiFi.DeviceConfigureInterface) then Exit;

 {Check Network Functions}
 if not Assigned(WiFi.Network.DeviceOpen) then Exit;
 if not Assigned(WiFi.Network.DeviceClose) then Exit;
 if not Assigned(WiFi.Network.DeviceRead) then
  begin
   if not Assigned(WiFi.Network.BufferReceive) then Exit;
   if not Assigned(WiFi.Network.BufferRelease) then Exit;
  end;
 if not Assigned(WiFi.Network.DeviceWrite) then
  begin
   if not Assigned(WiFi.Network.BufferAllocate) then Exit;
   if not Assigned(WiFi.Network.BufferTransmit) then Exit;
  end;
 
 {Check Optional Flags}
 //if Assigned(WiFi.DeviceRemainOnChannel) then //To Do
 // begin
 //  WiFi.WiFiFlags:=WiFi.WiFiFlags or WIFI_FLAG_HAS_REMAIN_ON_CHANNEL;
 // end;
 
 //if not Assigned(WiFi.DeviceSetKey) then //To Do
 // begin
 //  WiFi.WiFiFlags:=WiFi.WiFiFlags or WIFI_FLAG_IBSS_RSN;
 // end;
 
 {Check Optional Features}
 //if not Assigned(WiFi.DeviceHardwareScan) then //To Do
 // begin
 //  Result.WiFiFeatures:=Result.WiFiFeatures or WIFI_FEATURE_LOW_PRIORITY_SCAN or WIFI_FEATURE_AP_SCAN;
 // end;
 
 //To Do //IEEE80211_HW_QUEUE_CONTROL
 
 //To Do //WIFI_FEATURE_TDLS_CHANNEL_SWITCH
 
 {Check Report Rates}
 if WiFi.Hardware.MaxReportRates = 0 then
  begin
   WiFi.Hardware.MaxReportRates:=WiFi.Hardware.MaxRates;
  end;
  
 WiFi.RXChains:=1;
 
 {Make sure at least one band is configured}
 MaxRateCount:=0;
 ChannelCount:=0;
 HTSupported:=False;
 VHTSupported:=False;
 FillChar(ChannelDefinition,SizeOf(TIEEE80211ChannelDefinition),0);
 for Band:=0 to IEEE80211_NUM_BANDS - 1 do
  begin
   SupportedBand:=WiFi.Bands[Band];
   if SupportedBand <> nil then
    begin
     if ChannelDefinition.Channel = nil then
      begin
       {Initialize Default Channel}
       IEEE80211InitializeChannelDefinition(@ChannelDefinition,@SupportedBand.Channels[0],WIFI_CHAN_NO_HT);
       
       WiFi.Configuration.ChannelDefinition:=ChannelDefinition;
      end;
    
     Inc(ChannelCount,SupportedBand.ChannelCount);
     
     if MaxRateCount < SupportedBand.RateCount then
      begin
       MaxRateCount:=SupportedBand.RateCount;
      end;
      
     HTSupported:=HTSupported or SupportedBand.HTCapabilities.HTSupported;
     VHTSupported:=VHTSupported or SupportedBand.VHTCapabilities.VHTSupported;
     
     if SupportedBand.HTCapabilities.HTSupported then
      begin
       WiFi.RXChains:=Max(IEEE80211MCSToChains(@SupportedBand.HTCapabilities.MCS),WiFi.RXChains);
      end;
     
     {TODO: Consider VHT for RX chains}
    end;
  end;
 
 {If driver supports AP, also support VLAN}
 if (WiFi.InterfaceModes and (1 shl WIFI_IFTYPE_AP)) <> 0 then
  begin
   WiFi.InterfaceModes:=WiFi.InterfaceModes or (1 shl WIFI_IFTYPE_AP_VLAN);
  end;
  
 {Always supports monitor}
 WiFi.InterfaceModes:=WiFi.InterfaceModes or (1 shl WIFI_IFTYPE_MONITOR);
 
 //To Do //int_scan_req //See also //ieee80211_local//cfg80211_scan_request
 
 {If the underlying driver supports mesh, provide routing of mesh authentication frames}
 if (WiFi.InterfaceModes and (1 shl WIFI_IFTYPE_MESH_POINT)) <> 0 then
  begin
   WiFi.WiFiFlags:=WiFi.WiFiFlags or WIFI_FLAG_MESH_AUTH;
  end;
 
 {Support control port protocol changing}
 WiFi.WiFiFlags:=WiFi.WiFiFlags or WIFI_FLAG_CONTROL_PORT_PROTOCOL;
 
 if (WiFi.Hardware.Flags and IEEE80211_HW_SIGNAL_DBM) <> 0 then
  begin
   WiFi.SignalType:=WIFI_SIGNAL_TYPE_MBM;
  end
 else if (WiFi.Hardware.Flags and IEEE80211_HW_SIGNAL_UNSPEC) <> 0 then
  begin
   WiFi.SignalType:=WIFI_SIGNAL_TYPE_UNSPEC;
   if WiFi.Hardware.MaxSignal <= 0 then
    begin
     Result:=ERROR_INVALID_PARAMETER;
     Exit;
    end;
  end;
  
 {Calculate scan IE length} {Includes the DS Params, (extended) supported rates, and HT information}
 WiFi.ScanIELength:=4 + MaxRateCount {(ext) supp rates} + 3; {DS Params}
 if HTSupported then
  begin
   WiFi.ScanIELength:=WiFi.ScanIELength + 2 + SizeOf(TIEEE80211HTCapabilities);
  end;
 if VHTSupported then
  begin
   WiFi.ScanIELength:=WiFi.ScanIELength + 2 + SizeOf(TIEEE80211VHTCapabilities);
  end;
 //if not Assigned(WiFi.DeviceHardwareScan) then //To Do
 // begin
   {For DeviceHardwareScan, driver needs to set these}
   WiFi.MaxScanSSIDs:=4;
   WiFi.MaxScanIELength:=IEEE80211_MAX_DATA_LEN;
 // end;
 
 {If the driver supports any scan IEs, then assume the limit includes the standard IEs to add, otherwise leave it at zero}
 if WiFi.MaxScanIELength > 0 then
  begin
   WiFi.MaxScanIELength:=WiFi.MaxScanIELength - WiFi.ScanIELength;
  end;
  
 //To Do //ieee80211_cs_list_valid

 //To Do //ieee80211_init_cipher_suites
 
 //if not Assigned(WiFi.DeviceRemainOnChannel) then //To Do
 // begin
   WiFi.MaxRemainOnChannelDuration:=5000;
 // end;
  
 {Don't support internal TDLS setup}
 if (WiFi.WiFiFlags and WIFI_FLAG_SUPPORTS_TDLS) <> 0 then
  begin
   WiFi.WiFiFlags:=WiFi.WiFiFlags or WIFI_FLAG_TDLS_EXTERNAL_SETUP;
  end;
  
 //To Do //IEEE80211_HW_CHANCTX_STA_CSA
 
 WiFi.MaxCSACounters:=IEEE80211_MAX_CSA_COUNTERS_NUM;
 
 {The hardware needs headroom for sending the frame}
 WiFi.TXHeadroom:=Max(WiFi.Hardware.ExtraTXHeadroom,IEEE80211_TX_STATUS_HEADROOM);
 
 {If the driver doesn't specify a max listen interval use 5 which should be a safe default}
 if WiFi.Hardware.MaxListenInterval = 0 then
  begin
   WiFi.Hardware.MaxListenInterval:=5;
  end;
 WiFi.Configuration.ListenInterval:=WiFi.Hardware.MaxListenInterval;
 
 //To Do //dynamic_ps_forced_timeout
 
 //To Do //txq_ac_max_pending
 
 //To Do //ieee80211_wep_init
 
 WiFi.Hardware.Flags:=IEEE80211_CONF_IDLE;
 
 //To Do //ieee80211_led_init
 
 //To Do //ieee80211_init_rate_ctrl_alg
 
 {Add default STA interface if supported}
 if ((WiFi.InterfaceModes and (1 shl WIFI_IFTYPE_STATION)) <> 0) and ((WiFi.Hardware.Flags and IEEE80211_HW_NO_AUTO_VIF) = 0) then
  begin
   {$IFDEF WIFI_DEBUG}
   if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFi: Adding default STA interface');
   {$ENDIF}
   WiFi.Interrface.InterfaceType:=WIFI_IFTYPE_STATION;
   //To Do //ieee80211_if_add in \net\mac80211\iface.c
  end;
  
 //To Do //ieee80211_register_hw
 
 {Register Network}
 Result:=NetworkDeviceRegister(@WiFi.Network);
end;

{==============================================================================}

function WiFiDeviceDeregister(WiFi:PWiFiDevice):LongWord;
begin
 {}
 Result:=ERROR_INVALID_PARAMETER;

 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFi: Device deregister');
 {$ENDIF}

 {Check WiFi}
 if WiFi = nil then Exit;
 if WiFi.Network.Device.Signature <> DEVICE_SIGNATURE then Exit;
 
 //To Do //ieee80211_deregister_hw
 
 {Deregister Network}
 Result:=NetworkDeviceDeregister(@WiFi.Network);
end;

{==============================================================================}
{==============================================================================}
{WiFi Helper Functions}
function WiFiConfigurationIsHT(Configuration:PWiFiConfiguration):Boolean;
{conf_is_ht}
begin
 {}
 Result:=False;
 
 if Configuration = nil then Exit;
 
 Result:=(Configuration.ChannelDefinition.Width <> WIFI_CHAN_WIDTH_5) and (Configuration.ChannelDefinition.Width <> WIFI_CHAN_WIDTH_10) and (Configuration.ChannelDefinition.Width <> WIFI_CHAN_WIDTH_20_NOHT);
end;

{==============================================================================}

function WiFiConfigurationIsHT20(Configuration:PWiFiConfiguration):Boolean;
{conf_is_ht20}
begin
 {}
 Result:=False;
 
 if Configuration = nil then Exit;
 
 Result:=(Configuration.ChannelDefinition.Width = WIFI_CHAN_WIDTH_20);
end;
 
{==============================================================================}

function WiFiConfigurationIsHT40(Configuration:PWiFiConfiguration):Boolean;
{conf_is_ht40}
begin
 {}
 Result:=False;
 
 if Configuration = nil then Exit;
 
 Result:=(Configuration.ChannelDefinition.Width = WIFI_CHAN_WIDTH_40);
end;

{==============================================================================}

function WiFiConfigurationIsHT40Plus(Configuration:PWiFiConfiguration):Boolean;
{conf_is_ht40_plus}
begin
 {}
 Result:=False;
 
 if Configuration = nil then Exit;
 
 Result:=(Configuration.ChannelDefinition.Width = WIFI_CHAN_WIDTH_40) and (Configuration.ChannelDefinition.CenterFrequency1 > Configuration.ChannelDefinition.Channel.CenterFrequency); 
end;

{==============================================================================}

function WiFiConfigurationIsHT40Minus(Configuration:PWiFiConfiguration):Boolean;
{conf_is_ht40_minus}
begin
 {}
 Result:=False;
 
 if Configuration = nil then Exit;
 
 Result:=(Configuration.ChannelDefinition.Width = WIFI_CHAN_WIDTH_40) and (Configuration.ChannelDefinition.CenterFrequency1 < Configuration.ChannelDefinition.Channel.CenterFrequency); 
end;

{==============================================================================}

procedure WiFiNetworkDeviceAdd(Event:PWiFiDeviceEvent);
var
 Adapter:TNetworkAdapter;
begin
 {}
 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFi: Network device add');
 {$ENDIF}
 
 {Check Event}
 if Event = nil then Exit;
 if Event.Device = nil then Exit;
 
 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFi:  Device = ' + DeviceGetName(@Event.Device.Device));
 {$ENDIF}
 
 {Destroy Timer}
 if Event.Timer <> INVALID_HANDLE_VALUE then
  begin
   TimerDestroy(Event.Timer);
   Event.Timer:=INVALID_HANDLE_VALUE;
  end;

 {Check Managers}
 if AdapterManager = nil then Exit;
 if TransportManager = nil then Exit;

 {Check Settings}
 if NetworkSettings = nil then Exit;
 
 {Check Started}
 if WiFiStarted then
  begin
   {Check Type}
   case Event.Device.Device.DeviceType of
    NETWORK_TYPE_80211:begin
      {Check Adapter}
      Adapter:=AdapterManager.GetAdapterByDevice(Event.Device,False,NETWORK_LOCK_NONE); {Do not lock}
      if Adapter = nil then
       begin
        {Create Adapter}
        if NetworkSettings.GetBooleanDefault('WIRELESS_NETWORK_ENABLED',WIRELESS_NETWORK_ENABLED) then 
         begin
          Adapter:=TWiFiAdapter.Create(AdapterManager,Event.Device,DeviceGetName(@Event.Device.Device));
      
          {Check Adapter}
          if not NetworkSettings.GetBoolean(Adapter.Name + '_DISABLED') then
           begin
            {Start Adapter}
            Adapter.StartAdapter;
            
            {Bind Transports}
            TransportManager.BindTransports(Adapter);
            
            {Bind Monitors}
            TransportManager.BindMonitors(Adapter);
            
            {Bind Authenticators}
            TransportManager.BindAuthenticators(Adapter);
           end; 
         end; 
       end; 
     end;
   end; 
  end;  
end;

{==============================================================================}

function WiFiNetworkDeviceRemove(Network:PNetworkDevice):LongWord;
var
 Adapter:TNetworkAdapter;
begin
 {}
 Result:=ERROR_INVALID_PARAMETER;
 
 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFi: Network device remove');
 {$ENDIF}
 
 {Check Network}
 if Network = nil then Exit;

 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFi:  Device = ' + DeviceGetName(@Network.Device));
 {$ENDIF}
 
 {Check Managers}
 if AdapterManager = nil then Exit;
 if TransportManager = nil then Exit;

 {Check Started}
 if WiFiStarted then
  begin
   {Check Type}
   case Network.Device.DeviceType of
    NETWORK_TYPE_80211:begin
      {Check Adapter}
      Adapter:=AdapterManager.GetAdapterByDevice(Network,True,NETWORK_LOCK_READ);
      if Adapter <> nil then
       begin
        {Unbind Authenticators}
        TransportManager.UnbindAuthenticators(Adapter);
       
        {Unbind Monitors}
        TransportManager.UnbindMonitors(Adapter);

        {Unbind Transports}
        TransportManager.UnbindTransports(Adapter);
        
        {Stop Adapter}
        Adapter.StopAdapter;
        
        {Unlock Adapter}
        Adapter.ReaderUnlock;
        
        {Free Adapter}
        Adapter.Free;
       end; 
     end;
   end; 
  end;  
  
 {Return Result}
 Result:=ERROR_SUCCESS;    
end;

{==============================================================================}

function WiFiNetworkDeviceEnum(Network:PNetworkDevice;Data:Pointer):LongWord;
var
 Adapter:TNetworkAdapter;
begin
 {}
 Result:=ERROR_SUCCESS;
 
 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFi: Network device enumeration');
 {$ENDIF}

 {Check Network}
 if Network = nil then Exit;
 
 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFi:  Device = ' + DeviceGetName(@Network.Device));
 {$ENDIF}
 
 {Check Manager}
 if AdapterManager = nil then Exit;

 {Check Settings}
 if NetworkSettings = nil then Exit;
 
 {Check Started}
 if not WiFiStarted then
  begin
   {Check Type}
   case Network.Device.DeviceType of
    NETWORK_TYPE_80211:begin
      {Check Adapter}
      Adapter:=AdapterManager.GetAdapterByDevice(Network,False,NETWORK_LOCK_NONE); {Do not lock}
      if Adapter = nil then
       begin
        {Create Adapter}
        if NetworkSettings.GetBooleanDefault('WIRELESS_NETWORK_ENABLED',WIRELESS_NETWORK_ENABLED) then 
         begin
          TWiFiAdapter.Create(AdapterManager,Network,DeviceGetName(@Network.Device));
         end; 
       end; 
     end;
   end; 
  end;  
end;
 
{==============================================================================}

function WiFiNetworkDeviceNotify(Device:PDevice;Data:Pointer;Notification:LongWord):LongWord;
var
 Adapter:TNetworkAdapter;
 Event:PWiFiDeviceEvent;
begin
 {}
 Result:=ERROR_SUCCESS;
 
 {$IFDEF WIFI_DEBUG}
 if NETWORK_LOG_ENABLED then NetworkLogDebug(nil,'WiFi: Network device notification (Notification=' + NotificationToString(Notification) + ')');
 {$ENDIF}
 
 {Check Device}
 if Device = nil then Exit;
 
 {Check Manager}
 if AdapterManager = nil then Exit;

 {Check Notification}
 if (Notification and DEVICE_NOTIFICATION_REGISTER) <> 0 then
  begin
   {Check Started}
   if WiFiStarted then
    begin
     {Check Adapter}
     Adapter:=AdapterManager.GetAdapterByDevice(PNetworkDevice(Device),False,NETWORK_LOCK_NONE); {Do not lock}
     if Adapter = nil then
      begin
       {Create Event}
       Event:=AllocMem(SizeOf(TWiFiDeviceEvent));
       if Event = nil then Exit;
       
       {Setup Event}
       Event.Timer:=INVALID_HANDLE_VALUE;
       Event.Device:=PNetworkDevice(Device);
       
       {Create Timer}
       Event.Timer:=TimerCreateEx(WIFI_DEVICE_TIMER_INTERVAL,TIMER_STATE_ENABLED,TIMER_FLAG_WORKER,TTimerEvent(WiFiNetworkDeviceAdd),Event);
       if Event.Timer = INVALID_HANDLE_VALUE then
        begin
         {Destroy Event}
         FreeMem(Event);
        end;
      end;
    end;
  end
 else if (Notification and DEVICE_NOTIFICATION_CLOSING) <> 0 then
  begin
   {Check Started}
   if WiFiStarted then
    begin
     {Check Adapter}
     Adapter:=AdapterManager.GetAdapterByDevice(PNetworkDevice(Device),False,NETWORK_LOCK_NONE); {Do not lock}
     if Adapter <> nil then
      begin
       {Remove Adapter}
       WiFiNetworkDeviceRemove(PNetworkDevice(Device));
      end;
    end;
  end
 else if (Notification and DEVICE_NOTIFICATION_DEREGISTER) <> 0 then
  begin
   {Check Started}
   if WiFiStarted then
    begin
     {Check Adapter}
     Adapter:=AdapterManager.GetAdapterByDevice(PNetworkDevice(Device),False,NETWORK_LOCK_NONE); {Do not lock}
     if Adapter <> nil then
      begin
       {Remove Adapter}
       WiFiNetworkDeviceRemove(PNetworkDevice(Device));
      end;
    end;
  end;
end;
 
{==============================================================================}
{==============================================================================}
{IEEE80211 Helper Functions}
function IEEE80211HasToDS(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FCTL_TODS is set}
{ieee80211_has_tods}
begin
 {}
 Result:=(FrameControl and IEEE80211_FCTL_TODS) <> 0;
end;

{==============================================================================}

function IEEE80211HasFromDS(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FCTL_FROMDS is set}
{ieee80211_has_fromds}
begin
 {}
 Result:=(FrameControl and IEEE80211_FCTL_FROMDS) <> 0;
end;

{==============================================================================}

function IEEE80211HasA4(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FCTL_TODS and IEEE80211_FCTL_FROMDS are set}
{ieee80211_has_a4}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_TODS or IEEE80211_FCTL_FROMDS)) = (IEEE80211_FCTL_TODS or IEEE80211_FCTL_FROMDS);
end;

{==============================================================================}

function IEEE80211HasMoreFrags(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FCTL_MOREFRAGS is set}
{ieee80211_has_morefrags}
begin
 {}
 Result:=(FrameControl and IEEE80211_FCTL_MOREFRAGS) <> 0;
end;

{==============================================================================}

function IEEE80211HasRetry(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FCTL_RETRY is set}
{ieee80211_has_retry}
begin
 {}
 Result:=(FrameControl and IEEE80211_FCTL_RETRY) <> 0;
end;

{==============================================================================}

function IEEE80211HasPM(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FCTL_PM is set}
{ieee80211_has_pm}
begin
 {}
 Result:=(FrameControl and IEEE80211_FCTL_PM) <> 0;
end;

{==============================================================================}

function IEEE80211HasMoreData(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FCTL_MOREDATA is set}
{ieee80211_has_moredata}
begin
 {}
 Result:=(FrameControl and IEEE80211_FCTL_MOREDATA) <> 0;
end;

{==============================================================================}

function IEEE80211HasProtected(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FCTL_PROTECTED is set}
{ieee80211_has_protected}
begin
 {}
 Result:=(FrameControl and IEEE80211_FCTL_PROTECTED) <> 0;
end;

{==============================================================================}

function IEEE80211HasOrder(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FCTL_ORDER is set}
{ieee80211_has_order}
begin
 {}
 Result:=(FrameControl and IEEE80211_FCTL_ORDER) <> 0;
end;

{==============================================================================}

function IEEE80211IsMgmt(FrameControl:Word):Boolean; inline;
{Check if type is IEEE80211_FTYPE_MGMT}
{ieee80211_is_mgmt}
begin
 {}
 Result:=(FrameControl and IEEE80211_FCTL_FTYPE) = IEEE80211_FTYPE_MGMT;
end;

{==============================================================================}

function IEEE80211IsCtl(FrameControl:Word):Boolean; inline;
{Check if type is IEEE80211_FTYPE_CTL}
{ieee80211_is_ctl}
begin
 {}
 Result:=(FrameControl and IEEE80211_FCTL_FTYPE) = IEEE80211_FTYPE_CTL;
end;

{==============================================================================}

function IEEE80211IsData(FrameControl:Word):Boolean; inline;
{Check if type is IEEE80211_FTYPE_DATA}
{ieee80211_is_data}
begin
 {}
 Result:=(FrameControl and IEEE80211_FCTL_FTYPE) = IEEE80211_FTYPE_DATA;
end;

{==============================================================================}

function IEEE80211IsDataQoS(FrameControl:Word):Boolean; inline;
{Check if type is IEEE80211_FTYPE_DATA and IEEE80211_STYPE_QOS_DATA is set}
{ieee80211_is_data_qos}
begin
 {}
 {Mask with QOS_DATA rather than IEEE80211_FCTL_STYPE as we just need to check the one bit}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_STYPE_QOS_DATA)) = (IEEE80211_FTYPE_DATA or IEEE80211_STYPE_QOS_DATA);
end;

{==============================================================================}

function IEEE80211IsDataPresent(FrameControl:Word):Boolean; inline;
{Check if type is IEEE80211_FTYPE_DATA and has data}
{ieee80211_is_data_present}
begin
 {}
 {Mask with 0x40 and test that that bit is clear to only return true for the data-containing substypes}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or $40)) = IEEE80211_FTYPE_DATA;
end;

{==============================================================================}

function IEEE80211IsAssocReq(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_REQ}
{ieee80211_is_assoc_req}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_ASSOC_REQ);
end;

{==============================================================================}

function IEEE80211IsAssocResp(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_RESP}
{ieee80211_is_assoc_resp}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_ASSOC_RESP); 
end;

{==============================================================================}

function IEEE80211IsReassocReq(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_REQ}
{ieee80211_is_reassoc_req}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_REASSOC_REQ); 
end;

{==============================================================================}

function IEEE80211IsReassocResp(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_RESP}
{ieee80211_is_reassoc_resp}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_REASSOC_RESP); 
end;

{==============================================================================}

function IEEE80211IsProbeReq(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ}
{ieee80211_is_probe_req}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_PROBE_REQ); 
end;

{==============================================================================}

function IEEE80211IsProbeResp(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_RESP}
{ieee80211_is_probe_resp}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_PROBE_RESP); 
end;

{==============================================================================}

function IEEE80211IsBeacon(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_BEACON}
{ieee80211_is_beacon}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_BEACON); 
end;

{==============================================================================}

function IEEE80211IsATIM(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM}
{ieee80211_is_atim}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_ATIM); 
end;

{==============================================================================}

function IEEE80211IsDisassoc(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DISASSOC}
{ieee80211_is_disassoc}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_DISASSOC);
end;

{==============================================================================}

function IEEE80211IsAuth(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_AUTH}
{ieee80211_is_auth}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_AUTH); 
end;

{==============================================================================}

function IEEE80211IsDeauth(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DEAUTH}
{ieee80211_is_deauth}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_DEAUTH); 
end;

{==============================================================================}

function IEEE80211IsAction(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ACTION}
{ieee80211_is_action}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_MGMT or IEEE80211_STYPE_ACTION); 
end;

{==============================================================================}

function IEEE80211IsBackReq(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK_REQ}
{ieee80211_is_back_req}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_CTL or IEEE80211_STYPE_BACK_REQ); 
end;

{==============================================================================}

function IEEE80211IsBack(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK}
{ieee80211_is_back}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_CTL or IEEE80211_STYPE_BACK); 
end;

{==============================================================================}

function IEEE80211IsPSPoll(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_PSPOLL}
{ieee80211_is_pspoll}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_CTL or IEEE80211_STYPE_PSPOLL); 
end;

{==============================================================================}

function IEEE80211IsRTS(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_RTS}
{ieee80211_is_rts}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_CTL or IEEE80211_STYPE_RTS); 
end;

{==============================================================================}

function IEEE80211IsCTS(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CTS}
{ieee80211_is_cts}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_CTL or IEEE80211_STYPE_CTS); 
end;

{==============================================================================}

function IEEE80211IsACK(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_ACK}
{ieee80211_is_ack}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_CTL or IEEE80211_STYPE_ACK); 
end;

{==============================================================================}

function IEEE80211IsCFEnd(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFEND}
{ieee80211_is_cfend}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_CTL or IEEE80211_STYPE_CFEND); 
end;

{==============================================================================}

function IEEE80211IsCFEndAck(FrameControl:Word):Boolean; inline;
{Check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFENDACK}
{ieee80211_is_cfendack}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_CTL or IEEE80211_STYPE_CFENDACK); 
end;

{==============================================================================}

function IEEE80211IsNullFunc(FrameControl:Word):Boolean; inline;
{Check if frame is a regular (non-QoS) nullfunc frame}
{ieee80211_is_nullfunc}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_DATA or IEEE80211_STYPE_NULLFUNC); 
end;

{==============================================================================}

function IEEE80211IsQoSNullFunc(FrameControl:Word):Boolean; inline;
{Check if frame is a QoS nullfunc frame}
{ieee80211_is_qos_nullfunc}
begin
 {}
 Result:=(FrameControl and (IEEE80211_FCTL_FTYPE or IEEE80211_FCTL_STYPE)) = (IEEE80211_FTYPE_DATA or IEEE80211_STYPE_QOS_NULLFUNC); 
end;

{==============================================================================}

function IEEE80211IsBufferableMMPDU(FrameControl:Word):Boolean; inline;
{Check if frame is bufferable MMPDU}
{ieee80211_is_bufferable_mmpdu}
begin
 {}
 {IEEE 802.11-2012, definition of "bufferable management frame" note that this ignores the IBSS special case}
 Result:=IEEE80211IsMgmt(FrameControl) and (IEEE80211IsAction(FrameControl) or IEEE80211IsDisassoc(FrameControl) or IEEE80211IsDeauth(FrameControl)); 
end;

{==============================================================================}

function IEEE80211IsFirstFrag(SequenceControl:Word):Boolean; inline;
{Check if IEEE80211_SCTL_FRAG is not set}
{ieee80211_is_first_frag}
begin
 {}
 Result:=(SequenceControl and IEEE80211_SCTL_FRAG) = 0;
end;

{==============================================================================}

function IEEE80211HeaderLength(FrameControl:Word):LongWord;
{ieee80211_hdrlen}
var
 HeaderLength:LongWord;
begin
 {}
 HeaderLength:=24;
 
 if IEEE80211IsData(FrameControl) then
  begin
   if IEEE80211HasA4(FrameControl) then
    begin
     HeaderLength:=30;
    end;
   if IEEE80211IsDataQoS(FrameControl) then
    begin
     HeaderLength:=HeaderLength + IEEE80211_QOS_CTL_LEN;
     if IEEE80211HasOrder(FrameControl) then
      begin
       HeaderLength:=HeaderLength + IEEE80211_HT_CTL_LEN;
      end;
    end;
   
   Result:=HeaderLength;
   Exit;
  end;
  
 if IEEE80211IsMgmt(FrameControl) then
  begin
   if IEEE80211HasOrder(FrameControl) then
    begin
     HeaderLength:=HeaderLength + IEEE80211_HT_CTL_LEN;
    end;
  
   Result:=HeaderLength;
   Exit;
  end;
 
 if IEEE80211IsCtl(FrameControl) then
  begin
   {ACK and CTS are 10 bytes, all others 16. To see how
    to get this condition consider
      subtype mask:   0b0000000011110000 (0x00F0)
      ACK subtype:    0b0000000011010000 (0x00D0)
      CTS subtype:    0b0000000011000000 (0x00C0)
      bits that matter:         ^^^      (0x00E0)
      value of those: 0b0000000011000000 (0x00C0)}
   if FrameControl and $00E0 = $00C0 then
    begin
     HeaderLength:=10;
    end
   else
    begin
     HeaderLength:=16;
    end;
  end;
 
 Result:=HeaderLength;
end;

{==============================================================================}

function IEEE80211HeaderLengthFromBuffer(Data:Pointer;Size:LongWord):LongWord;
{ieee80211_get_hdrlen_from_skb}
var
 HeaderLength:LongWord;
begin
 {}
 Result:=0;
 
 if Data = nil then Exit;
 if Size < 10 then Exit;
 
 HeaderLength:=IEEE80211HeaderLength(PIEEE80211Header(Data).FrameControl);
 if HeaderLength > Size then Exit;
 
 Result:=HeaderLength;
end;

{==============================================================================}

function IEEE80211MCSToChains(MCS:PIEEE80211MCSInfo):Byte;
{ieee80211_mcs_to_chains}
begin
 {}
 Result:=1;
 
 if MCS = nil then exit;
 
 {TODO: consider RXHighest}
 if MCS.RXMask[3] <> 0 then
  begin
   Result:=4;
  end
 else if MCS.RXMask[2] <> 0 then
  begin
   Result:=3;
  end
 else if MCS.RXMask[1] <> 0 then  
  begin
   Result:=2;
  end;
end;

{==============================================================================}

function IEEE80211ChannelToFrequency(Channel:Integer;Band:LongWord):Integer;
{ieee80211_channel_to_frequency}
begin
 {}
 Result:=0;
 
 {See 802.11 17.3.8.3.2 and Annex J there are overlapping channel numbers in 5GHz and 2GHz bands}
 if Channel <= 0 then Exit; {Not Supported}
 
 case Band of
  IEEE80211_BAND_2GHZ:begin
    if Channel = 14 then
     begin
      Result:=2484;
     end            
    else if Channel < 14 then
     begin
      Result:=2407 + Channel * 5;
     end; 
   end;              
  IEEE80211_BAND_5GHZ:begin
    if (Channel >= 182) and (Channel <= 196) then
     begin
      Result:=4000 + Channel * 5;
     end 
    else
     begin
      Result:=5000 + Channel * 5;
     end; 
   end;              
  IEEE80211_BAND_60GHZ:begin
    if Channel < 5 then
     begin     
      Result:=56160 + Channel * 2160;
     end; 
   end;
 end;
end;

{==============================================================================}

function IEEE80211FrequencyToChannel(Frequency:Integer):Integer;
{ieee80211_frequency_to_channel}
begin
 {}
 {See 802.11 17.3.8.3.2 and Annex J}
 if Frequency = 2484 then
  begin
   Result:=14;
  end 
 else if Frequency < 2484 then
  begin
   Result:=(Frequency - 2407) div 5;
  end 
 else if (Frequency >= 4910) and (Frequency <= 4980) then
  begin
   Result:=(Frequency - 4000) div 5;
  end 
 else if Frequency <= 45000 then {DMG band lower limit}
  begin
   Result:=(Frequency - 5000) div 5;
  end 
 else if (Frequency >= 58320) and (Frequency <= 64800) then
  begin
   Result:=(Frequency - 56160) div 2160;
  end 
 else
  begin
   Result:=0; 
  end;
end;

{==============================================================================}

procedure IEEE80211InitializeChannelDefinition(Definition:PIEEE80211ChannelDefinition;Channel:PIEEE80211Channel;ChannelType:LongWord);
{cfg80211_chandef_create}
begin
 {}
 if Definition = nil then exit;
 if Channel = nil then Exit;
 
 Definition.Channel:=Channel;
 Definition.CenterFrequency2:=0;
 
 case ChannelType of
  WIFI_CHAN_NO_HT:begin
    Definition.Width:=WIFI_CHAN_WIDTH_20_NOHT;
    Definition.CenterFrequency1:=Channel.CenterFrequency;
   end;
  WIFI_CHAN_HT20:begin
    Definition.Width:=WIFI_CHAN_WIDTH_20;
    Definition.CenterFrequency1:=Channel.CenterFrequency;
   end;
  WIFI_CHAN_HT40PLUS:begin
    Definition.Width:=WIFI_CHAN_WIDTH_40;
    Definition.CenterFrequency1:=Channel.CenterFrequency + 10;
   end;
  WIFI_CHAN_HT40MINUS:begin
    Definition.Width:=WIFI_CHAN_WIDTH_40;
    Definition.CenterFrequency1:=Channel.CenterFrequency - 10;
   end;
  else
   begin
    if NETWORK_LOG_ENABLED then NetworkLogError(nil,'WiFi: Invalid ChannelType=' + IntToStr(ChannelType));
   end;   
 end;
end;

{==============================================================================}

function IEEE80211FindInformationElement(Identifier:Byte;InformationElement:PByte;ElementLength:LongWord):PByte;
{cfg80211_find_ie}
begin
 {}
 Result:=nil;
 
 {Check Element}
 if InformationElement = nil then Exit;
 
 while (ElementLength > 2) and (InformationElement[0] <> Identifier) do
  begin
   Dec(ElementLength,InformationElement[1] + 2);
   Inc(InformationElement,InformationElement[1] + 2);
  end;
  
 if ElementLength < 2 then Exit;
 if ElementLength < (2 + InformationElement[1]) then Exit;
  
 Result:=InformationElement;
end;

{==============================================================================}

function IEEE80211ParseInformationElements(Buffer:Pointer;Size:LongWord;Action:Boolean;Elements:PIEEE80211InformationElements):Boolean;
{ieee802_11_parse_elems_crc}
var
 Current:PByte;
 Remain:LongWord;
 Identifier:Byte;
 ElementLength:Byte;
 ParseError:Boolean;
 ParseFailed:Boolean;
 InformationElement:PByte;
begin
 {}
 Result:=False;
 
 {Check Buffer}
 if Buffer = nil then Exit;
 
 {Check Elements}
 if Elements = nil then Exit;
 
 {Setup Elements}
 FillChar(Elements^,SizeOf(TIEEE80211InformationElements),0);
 Elements.Address:=Buffer;
 Elements.Size:=Size;
 
 {Parse Elements}
 Remain:=Size;
 Current:=Buffer;
 ParseError:=False;
 while Remain >= 2 do
  begin
   {Get Identifier}
   Identifier:=Current^;
   Inc(Current);
   
   {Get Length}
   ElementLength:=Current^;
   Inc(Current);
   
   {Check Remain}
   Dec(Remain,2);
   if ElementLength > Remain then
    begin
     ParseError:=True;
     Break;
    end;
    
   ParseFailed:=False;
   
   case Identifier of 
	WLAN_EID_LINK_ID:begin
      if (ElementLength + 2) <> SizeOf(TIEEE80211TDLSLinkIdentifierIE) then
       begin
        ParseFailed:=True;
       end
      else
       begin
        Elements.LinkIdentifier:=PIEEE80211TDLSLinkIdentifierIE(Current - 2);
       end;
     end;  
    WLAN_EID_CHAN_SWITCH_TIMING:begin
      if ElementLength <> SizeOf(TIEEE80211ChannelSwitchTiming) then
       begin
        ParseFailed:=True;
       end
      else
       begin
        Elements.ChannelSwitchTiming:=PIEEE80211ChannelSwitchTiming(Current);
       end;       
     end;
    WLAN_EID_EXT_CAPABILITY:begin
      Elements.ExtendedCapabilities:=Current;
      Elements.ExtendedCapabilitiesLength:=ElementLength;
     end;
    WLAN_EID_SSID:begin
      Elements.SSID:=Current;
      Elements.SSIDLength:=ElementLength;
     end;
    WLAN_EID_SUPP_RATES:begin
      Elements.SupportedRates:=Current;
      Elements.SupportedRatesLength:=ElementLength;
     end;
    WLAN_EID_DS_PARAMS:begin
      if ElementLength >= 1 then
       begin
        Elements.DSParameters:=Current;
       end
      else
       begin
        ParseFailed:=True;
       end;       
     end;
    WLAN_EID_TIM:begin
      if ElementLength >= SizeOf(TIEEE80211TrafficIndicationMapIE) then
       begin
        Elements.TrafficIndicationMap:=PIEEE80211TrafficIndicationMapIE(Current);
        Elements.TrafficIndicationMapLength:=ElementLength;
       end
      else
       begin
        ParseFailed:=True;
       end;       
     end;
    WLAN_EID_CHALLENGE:begin
      Elements.ChallengeText:=Current;
      Elements.ChallengeTextLength:=ElementLength;
     end;
    WLAN_EID_VENDOR_SPECIFIC:begin
      {Microsoft OUI (00:50:F2)}
      if (ElementLength >= 4) and (Current[0] = $00) and (Current[1] = $50) and (Current[2] = $f2) then
       begin
        {OUI Type 2 - WMM IE}
        if (ElementLength >= 5) and (Current[3] = 2) then
         begin
          if Current[4] = 0 then
           begin
            Elements.WMMInfo:=Current;
            Elements.WMMInfoLength:=ElementLength;
           end
          else if Current[4] = 1 then
           begin
            Elements.WMMParam:=Current;
            Elements.WMMParamLength:=ElementLength;
           end;
         end
        
       end;
     end;
    WLAN_EID_RSN:begin
      Elements.RSN:=Current;
      Elements.RSNLength:=ElementLength;
     end;
    WLAN_EID_ERP_INFO:
      if ElementLength >= 1 then
       begin
        Elements.ERPInfo:=Current;
       end
      else
       begin
        ParseFailed:=True;
       end;       
    WLAN_EID_EXT_SUPP_RATES:begin
      Elements.ExtendedSupportedRates:=Current;
      Elements.ExtendedSupportedRatesLength:=ElementLength;
     end;
    WLAN_EID_HT_CAPABILITY:begin
      if ElementLength >= SizeOf(TIEEE80211HTCapabilities) then
       begin
        Elements.HTCapabilities:=PIEEE80211HTCapabilities(Current);
       end
      else
       begin
        ParseFailed:=True;
       end;       
     end;
    WLAN_EID_HT_OPERATION:begin
      if ElementLength >= SizeOf(TIEEE80211HTOperation) then
       begin
        Elements.HTOperation:=PIEEE80211HTOperation(Current);
       end
      else
       begin
        ParseFailed:=True;
       end;       
     end;
    WLAN_EID_VHT_CAPABILITY:begin
      if ElementLength >= SizeOf(TIEEE80211VHTCapabilities) then
       begin
        Elements.VHTCapabilities:=PIEEE80211VHTCapabilities(Current);
       end
      else
       begin
        ParseFailed:=True;
       end;       
     end;
    WLAN_EID_VHT_OPERATION:begin
      if ElementLength >= SizeOf(TIEEE80211VHTOperation) then
       begin
        Elements.VHTOperation:=PIEEE80211VHTOperation(Current);
       end
      else
       begin
        ParseFailed:=True;
       end;       
     end;
    WLAN_EID_OPMODE_NOTIF:begin
      if ElementLength > 0 then
       begin
        Elements.OpmodeNotification:=Current;
       end
      else
       begin
        ParseFailed:=True;
       end;       
     end;
    WLAN_EID_MESH_ID:begin
      Elements.MeshID:=Current;
      Elements.MeshIDLength:=ElementLength;
     end;
    WLAN_EID_MESH_CONFIG:begin
      if ElementLength >= SizeOf(TIEEE80211MeshConfigurationIE) then
       begin
        Elements.MeshConfiguration:=PIEEE80211MeshConfigurationIE(Current);
       end
      else
       begin
        ParseFailed:=True;
       end;       
     end;    
    WLAN_EID_PEER_MGMT:begin
      Elements.PeerManagement:=Current;
      Elements.PeerManagementLength:=ElementLength;
     end;
    WLAN_EID_MESH_AWAKE_WINDOW:begin
      if ElementLength >= 2 then
       begin
        Elements.MeshAwakeWindow:=PWord(Current)^;
       end;
     end;
    WLAN_EID_PREQ:begin
      Elements.PREQ:=Current;
      Elements.PREQLength:=ElementLength;
     end;
    WLAN_EID_PREP:begin
      Elements.PREP:=Current;
      Elements.PREPLength:=ElementLength;
     end;
    WLAN_EID_PERR:begin
      Elements.PERR:=Current;
      Elements.PERRLength:=ElementLength;
     end;
    WLAN_EID_RANN:begin
      if ElementLength >= SizeOf(TIEEE80211RootAnnouncementIE) then
       begin
        Elements.RootAnnouncement:=PIEEE80211RootAnnouncementIE(Current);
       end
      else
       begin
        ParseFailed:=True;
       end;       
     end;
    WLAN_EID_CHANNEL_SWITCH:begin
      if ElementLength <> SizeOf(TIEEE80211ChannelSwitchIE) then
       begin
        ParseFailed:=True;
       end
      else
       begin
        Elements.ChannelSwitch:=PIEEE80211ChannelSwitchIE(Current);
       end;       
     end;
    WLAN_EID_EXT_CHANSWITCH_ANN:begin
      if ElementLength <> SizeOf(TIEEE80211ExtChannelSwitchIE) then
       begin
        ParseFailed:=True;
       end
      else
       begin
        Elements.ExtChannelSwitch:=PIEEE80211ExtChannelSwitchIE(Current);
       end;       
     end;
    WLAN_EID_SECONDARY_CHANNEL_OFFSET:begin
      if ElementLength <> SizeOf(TIEEE80211SecondaryChannelOffsetIE) then
       begin
        ParseFailed:=True;
       end
      else
       begin
        Elements.SecondaryChannelOffset:=PIEEE80211SecondaryChannelOffsetIE(Current);
       end;       
     end;
    WLAN_EID_CHAN_SWITCH_PARAM:begin
      if ElementLength <> SizeOf(TIEEE80211MeshChannelSwitchParamsIE) then
       begin
        ParseFailed:=True;
       end
      else
       begin
        Elements.MeshChannelSwitchParams:=PIEEE80211MeshChannelSwitchParamsIE(Current);
       end;       
     end;
    WLAN_EID_WIDE_BW_CHANNEL_SWITCH:begin
      if not(Action) or (ElementLength <> SizeOf(TIEEE80211WidebandChannelSwitchIE)) then
       begin
        ParseFailed:=True;
       end
      else
       begin
        Elements.WidebandChannelSwitch:=PIEEE80211WidebandChannelSwitchIE(Current);
       end;       
     end;
    WLAN_EID_CHANNEL_SWITCH_WRAPPER:begin
      if Action then
       begin
        ParseFailed:=True;
       end
      else
       begin
        {We only care about the wide bandwidth channel switch element, so parse it out manually}
        InformationElement:=IEEE80211FindInformationElement(WLAN_EID_WIDE_BW_CHANNEL_SWITCH,Current,ElementLength);
        if InformationElement <> nil then
         begin
          if InformationElement[1] = SizeOf(TIEEE80211WidebandChannelSwitchIE) then
           begin
            Elements.WidebandChannelSwitch:=PIEEE80211WidebandChannelSwitchIE(InformationElement + 2);
           end
          else
           begin
            ParseFailed:=True;
           end;
         end;
       end;
     end;
    WLAN_EID_COUNTRY:begin
      Elements.Country:=Current;
      Elements.CountryLength:=ElementLength;
     end;
    WLAN_EID_PWR_CONSTRAINT:begin
      if ElementLength <> 1 then
       begin
        ParseFailed:=True;
       end
      else
       begin
        Elements.PowerConstraint:=Current;
       end;
     end;
    WLAN_EID_CISCO_VENDOR_SPECIFIC:begin
      {Lots of different options exist, get the Dynamic Transmit Power Control element}
      if ElementLength < 4 then
       begin
        ParseFailed:=True;
       end
      else
       begin      
        {Cisco OUI and DTPC tag (0x00)}
        if (Current[0] = $00) and (Current[1] = $40) and (Current[2] = $96) and (Current[3] = $00) then
         begin
          if ElementLength <> 6 then
           begin
            ParseFailed:=True;
           end
          else
           begin
            Elements.CiscoDTPC:=Current;
           end;           
         end;
       end; 
     end;
    WLAN_EID_TIMEOUT_INTERVAL:begin
      if ElementLength >= SizeOf(TIEEE80211TimeoutIntervalIE) then
       begin
        Elements.TimeoutInterval:=PIEEE80211TimeoutIntervalIE(Current);
       end
      else
       begin
        ParseFailed:=True;
       end;       
     end;
   end;
   
   
   {Check Error}
   if ParseFailed then
    begin
     ParseError:=True;
    end;
    
   {Update Position}
   Dec(Remain,ElementLength);
   Inc(Current,ElementLength);
  end;
 
 if Remain <> 0 then
  begin
   ParseError:=True;
  end;
  
 Result:=not(ParseError);
end;

{==============================================================================}
{==============================================================================}

initialization
 WiFiInit;
 if NetworkStartCompleted then
  begin
   WiFiStart(nil,NETWORK_EVENT_SYSTEM_START);
  end;
  
{==============================================================================}
 
finalization
 {Nothing}

{==============================================================================}
{==============================================================================}

end.
